Skip to content

[adr] Alert handling with async handlers and the synchronous Alert API - #18283

Open
titusfortner wants to merge 5 commits into
SeleniumHQ:trunkfrom
titusfortner:adr-bidi-alert-handling
Open

titusfortner wants to merge 5 commits into
SeleniumHQ:trunkfrom
titusfortner:adr-bidi-alert-handling

Conversation

@titusfortner

@titusfortner titusfortner commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

📄 The decision, its rationale, considered options, and consequences are in the record file this PR adds; read it there. The sections below are proposal notes and review logistics.

🔗 Related

📝 Proposal notes

This proposal also includes updating the Selenium 5 plan to include it because without it existing alert handling user code can break without recourse.

This ADR is about how to handle alerts in general, but the biggest decision is going to be about how we want to manage Backwards compatibility (Decisions 3-5),
so I'm expounding on the options in the PR description here instead of blowing up the ADR.

Backwards Compatibility

There are 2 main backwards compatibility issues for working with Alerts in BiDi

  1. Async: BiDi spec implementations only support asynchronous alert handling, requiring translation (Options 2 & 3 below address this)
  2. Event Timing: The BiDi event is emitted when the prompt opens, not when a non-alert command is sent after the alert is open (Only option 3 below addresses this)

Options

  1. Async-only support
    • This is what WebdriverIO did
    • No unhandledPromptBehavior capability
    • No classic Alert API
    • Alerts auto-dismiss by default and a user must rewrite their alert handling code in an async handler to override this
    • Easiest for us to implement
  2. Best-effort synchronous support without "unhandled" behavior
    • No unhandledPromptBehavior capability (we could add a new capability to accept/dismiss alerts when they open)
    • User alert handling code doesn't need to change
    • Code will error when users do not explicitly handle an alert
    • Moderately difficult for us to implement
  3. Synchronous support with "unhandled" behavior
    • Keeps the concept of "unhandled alert" and the "next command" handling
    • Keeps the default behavior of "dismiss and notify"
    • Slightly more implementation requirements than Option 2, but mostly it will be harder to reason about edge cases while translating behaviors across 2 dimensions (timing and synchronicity)

The record proposes Option 3, but Option 2 might be better overall.

Implementation Considerations

Regardless of which option, Selenium must do these things:

Differences per option (assumes w3c/webdriver-bidi#1185 is merged)

1 2 3
Deprecate/Remove unhandledPromptBehavior in BiDi sessions ✓ ✓ –
Deprecate/Remove the Alert API in BiDi sessions ✓ – –
Track open alerts per context from prompt events – ✓ ✓
Track pending commands, and interrupt when an alert opens in its context – ✓ ✓
Deprecate/Remove support for maps in unhandledPromptBehavior – – ✓
Wrap non-Alert BiDi and classic commands with error handling – – ✓

🗣 Discussion

(TLC meeting notes linked here)

📌 Tracking

Tracking issue: (linked on acceptance)

@selenium-ci

Copy link
Copy Markdown
Member

/agentic_describe

@qodo-code-review

qodo-code-review Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Propose BiDi alert handlers with classic Alert API compatibility

📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Proposes asynchronous alert handlers while preserving synchronous alert access in BiDi sessions.
• Defines classic prompt behavior, handler precedence, and exception handling to avoid surprising
 changes for existing tests.
• Records rejected alternatives and the binding work needed to implement the proposal.
Diagram

graph TD
  E["BiDi prompt event"] --> S["Driver alert state"] --> H{"Handler registered?"}
  H -->|Yes| A["Newest async handler"]
  H -->|No| C["Default compatibility"] --> N["Next command"] --> P["Classic prompt policy"]
  C --> U["Synchronous Alert API"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Keep the Alert API without classic unhandled behavior
  • ➕ Preserves explicit synchronous alert handling with less next-command compatibility logic.
  • ➖ Changes configured prompt behavior and can leave unexpected alerts blocking subsequent commands.
2. Use asynchronous handlers only
  • ➕ Simplifies the binding-side alert model.
  • ➖ Requires existing tests to rewrite alert handling and can silently dismiss unexpected prompts.

Recommendation: The proposed compatibility path best meets the stated goal of enabling BiDi without rewriting classic tests. Reviewers should explicitly weigh that benefit against the complexity of reproducing next-command behavior and interrupting pending BiDi commands; the Alert-API-only alternative is the principal simpler option.

Files changed (1) +116 / -0

Documentation (1) +116 / -0
bidi-alert-handling.mdRecord proposed BiDi and classic alert-handling contract +116/-0

Record proposed BiDi and classic alert-handling contract

• Adds a proposed ADR for driver-level asynchronous handlers, synchronous Alert API compatibility, and classic unhandledPromptBehavior semantics. It defines handler precedence and error ownership, compares alternatives, and lists the alert tracking and command-handling work required of bindings. No implementation changes are included.

docs/decisions/bidi-alert-handling.md

@qodo-code-review

qodo-code-review Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can see how risky a PR is at a glance with blast radius labels

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit 011b6d3 ⏭️ Skipped

Results up to commit 5159122 ⏭️ Skipped


No changes from previous review

Results up to commit 431381a ⏭️ Skipped


No changes from previous review

Results up to commit deff9ab ⏭️ Skipped


No changes from previous review

Grey Divider

Qodo Logo

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit deff9ab

@@ -0,0 +1,116 @@
# 18283. Allow alert handling with async handlers and the synchronous Alert API

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should update the PR name with the same ADR name.

## Decision

1. **Alert handlers may be added or removed.** Two methods are made available directly on the driver object, and a registered handler applies to all contexts.
`addAlertHandler` returns a handle object; `removeAlertHandler` takes that handle and unregisters exactly that handler.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when handlers are removed?

  • If I add A, then B, then remove B, does A run again?
  • If I remove the only handler, does the classic behavior from decisions 3 and 4 come back? Decision 5 hints at yes ("until it is removed").

Can we say it works like a stack?

so it applies for a specific block of code.

2. **A handler is a callable that acts on the alert object.** Inside the async handler the user has access to an alert object
that describes its type and can read text, send keys, accept or dismiss just like in the current Alert API.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What can a handler call while the alert is open? If it runs a non-alert command (e.g. find an element), that command may block or fail. Should we say only alert methods are allowed in the handler?

all support for `unhandledPromptBehavior` values. Users need to manage alerts through their handler, and Alert API commands must error.

6. **Only the most recently added handler is called.** Unlike Network handlers, events are not passed along a chain,
any previously registered handlers are ignored (including the default handler) so users can scope handlers to specific alert instances.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"The default handler" isn't defined anywhere. I assume it's the classic behavior from decisions 3 and 4 (Alert API plus unhandledPromptBehavior). Can we name it once and use that name everywhere?


7. **Non-default handlers must manage disposition and errors.**
An exception in the callable is not caught or logged on behalf of the user, it is always surfaced.
An alert must not be closed for the user after the callable is executed; a handler may pass the alert object to the main thread to act on later.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a handler leaves the alert open, what does the next command do? The Alert API throws (decision 5), and falling back to unhandledPromptBehavior was rejected. So does it hang, or throw a specific error?

Also, if the handler passes the alert object to the main thread, does that object still work there while the Alert API throws? Both cases should be stated.



```ruby
handle = driver.add_alert_handler { |alert| alert.type == 'prompt' ? alert.accept : alert.dismiss }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With a handler registered, the default "accept beforeUnload" no longer applies. This example would dismiss a beforeUnload prompt and block navigation. Can we either update the example to handle it, or keep accepting beforeUnload when the handler doesn't act on it?

- Enabling BiDi requires no code changes
- Current BiDi Users:
- If users updated `unhandledPromptBehavior` value to a map, they no longer need that and should remove it
- Sessions that completed only because BiDi has automatically dismissed alerts will now get the errors.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree this is the right behavior, since it matches classic.

- Bindings must do a lot of work to compensate for what the remote end used to do
- Always set `unhandledPromptBehavior` to `{"default": "ignore"}` to allow client to handle all prompts
- Track open alerts and associated contexts
- Check for alerts before sending commands

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ADR seems to assume a mixed session: BiDi is on, but most commands still go over classic HTTP. It would help to say that explicitly in Context.

That leaves a timing gap. The BiDi "alert opened" event comes over the WebSocket, but classic commands go over HTTP, so a classic command can reach the browser before the binding gets the event. For example:

  1. A click opens an alert.
  2. The test sends findElement right away. The binding hasn't received the event yet, so it sees no open alert and sends the command.
  3. Since the remote end is set to ignore, it returns a plain "unexpected alert open" error instead of applying the user's configured behavior.

So checking tracked alerts before each command isn't enough. Bindings also need to catch "unexpected alert open" errors from classic commands and apply the configured unhandledPromptBehavior then. Can we add that to this list?

@selenium-ci

Copy link
Copy Markdown
Member

/agentic_review

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 011b6d3

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants