Skip to content

[Bug]: Auto-dismissed dialog race terminates one of two CDP clients #42827

Description

@Tib-Gridello

Version

1.63.0

Steps to reproduce

Minimal reproduction: https://github.com/Tib-Gridello/playwright-dialog-race-repro

  1. Clone the repository.
  2. Run npm install.
  3. Run npx playwright install chromium.
  4. Run npm run reproduce.

The reproduction starts Chromium with a CDP endpoint, attaches two independent Playwright processes to the same page, leaves the JavaScript dialog unclaimed in both clients, and triggers one alert.

Both Playwright processes receive the dialog and auto-dismiss it. One Page.handleJavaScriptDialog call wins; the other rejects because the dialog has already closed. In DialogManager.dialogDidOpen, the auto-dismiss path uses dialog._close().then(() => {}), so that rejection is unhandled and terminates the losing Playwright process.

This also reproduces on Playwright 1.60.0. The same auto-dismiss line is present on current main (07f1a6154795f055f341b8972086533e8e48b36f).

Expected behavior

The dialog is dismissed once and both Playwright clients remain connected. A failed best-effort auto-dismiss should not terminate the Playwright process when another CDP client already handled the dialog.

Actual behavior

One worker terminates deterministically on the first dialog:

ProtocolError: Protocol error (Page.handleJavaScriptDialog): No dialog is showing
    at Dialog._dismiss (.../playwright-core/lib/coreBundle.js:13274:20)
    at Dialog._close (.../playwright-core/lib/coreBundle.js:13287:22)
    at DialogManager.dialogDidOpen (.../playwright-core/lib/coreBundle.js:13308:18)

Node.js v26.5.0
worker 2 exited: code=1 signal=null

Additional context

Issue #36627 reported the same protocol error around Electron beforeunload dialogs, but it was closed without a minimal reproduction. This report isolates a different, deterministic trigger: two independent CDP clients racing Playwright's implicit dialog dismissal.

The explicit cleanup path in removeDialogHandler already uses dialog._close().catch(() => {}); the implicit auto-dismiss path uses .then(() => {}) without a rejection handler. I can submit the corresponding one-line fix and a two-client regression test if this is approved for community contribution.

Environment

System:
  OS: macOS 26.6.2
  CPU: (18) arm64 Apple M5 Max
  Memory: 2.34 GB / 48.00 GB
Binaries:
  Node: 26.5.0 - /opt/homebrew/bin/node
  npm: 11.17.0 - /opt/homebrew/bin/npm
npmPackages:
  playwright: 1.63.0 => 1.63.0

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions