Skip to content

[Regression]: middle / modifier click intermittently fails to open a new tab on Playwright 1.62.x #42142

Closed
Mahnoor-Zaffar/playwright
#1

Description

Last Good Version

1.61.1

First Bad Version

1.62.0

Steps to reproduce

  1. git clone https://github.com/YuriiMotov/playwright-1.62-new-tab-mre.git
  2. cd playwright-1.62-new-tab-mre
  3. npm install
  4. npx playwright --version - expected Version 1.62.1
  5. npx playwright test --repeat-each=25 --workers=16 - see several tests fail with Test timeout of 30000ms exceeded
  6. Downgrade to latest working version: npm pkg set devDependencies.@playwright/test=1.61.1 && npm install
  7. npx playwright --version - expected Version 1.61.1
  8. npx playwright test --repeat-each=25 --workers=16 - see tests pass

Expected behavior

I expect tests pass on 1.62.x as it was on 1.61.1 and previous versions.

Actual behavior

Several tests fail with Test timeout of 30000ms exceeded

Additional context

Only tests that attempt to open page in a new tab are affected.

--workers=16 here is to imitate overloaded machine. In fact in our case it fails with --workers=1 in CI.

See report generated by Claude Code: https://github.com/YuriiMotov/playwright-1.62-new-tab-mre/blob/master/README.md

Environment

System:
    OS: Linux 7.0 Ubuntu 24.04.4 LTS 24.04.4 LTS (Noble Numbat)
    CPU: (14) x64 Intel(R) Core(TM) Ultra 5 125U
    Memory: 28.49 GB / 46.52 GB
    Container: Yes
  Binaries:
    Node: 22.23.0 - /run/user/1000/fnm_multishells/1402339_1785955498713/bin/node
    npm: 10.9.8 - /run/user/1000/fnm_multishells/1402339_1785955498713/bin/npm
    bun: 1.3.8 - /home/yurii/.bun/bin/bun
  IDEs:
    VSCode: 1.124.2 - /usr/bin/code
  Languages:
    Bash: 5.2.21 - /usr/bin/bash
  npmPackages:
    @playwright/test: 1.62.1 => 1.62.1

Activity

  1. Mahnoor-Zaffar commented on Aug 6, 2026

    @Mahnoor-Zaffar

    Hey! Thanks for reporting this.

    The most likely root cause of this issue appears to be related to changes made in version 1.62.0 that affect the handling of new tab openings. While the specific code changes are not detailed in the issue, the problem is isolated to the functionality that manages new tab interactions. The affected code may reside in the page.ts file or related modules that handle browser interactions.

    A reasonable direction for fixing this issue would be to investigate the changes made in version 1.62.0 that relate to tab management and ensure that the logic for handling middle and modifier clicks is functioning as intended.

    If no one is currently working on this, I'd be happy to take a look and put together a fix.

  2. github-actions commented on Aug 6, 2026

    @github-actions
    Contributor

    Thanks for the exceptionally clean MRE — the version table and the "no JavaScript, pure browser default action" framing made this easy to confirm.

    Reproduced on 1.62.1, and it looks already fixed on tip-of-tree.

    What I ran:

    • 1.62.1 — your MRE unchanged, --repeat-each=25 --workers=16 on a 4-core box (heavy oversubscription): 2/50 failed with waitForURL: Test timeout of 30000ms exceeded. Matches your report.
    • tip-of-tree (1.63.0-next) — the same scenario condensed to a self-contained spec (middle-click and ControlOrMeta click, plain <a href>, context.waitForEvent('page') + waitForURL): 0 failures across 50 tests @ 16 workers and 100 tests @ 32 workers (8× oversubscription). Could not get it to fail.

    Both runs use the same default headless-shell binary, so it's an apples-to-apples comparison.

    The relevant difference is the bundled browser: 1.62.x ships Chromium chromium-1234, tip-of-tree ships chromium-1237. This is a browser-default-action bug (opening a background tab with nothing in the page to interfere), and it disappears with the newer Chromium roll — consistent with an upstream fix that came in after 1.62.x was cut.

    So: nothing to change in the Playwright test code — the intermittent "new tab never opens / never navigates" behavior is gone on the current Chromium. It should ride out with the next release that carries chromium-1237. If you can, giving @playwright/test@next a spin against your real app would be a useful confirmation from your side.

    Triaged by the Playwright bot - agent run

  3. YuriiMotov commented on Aug 6, 2026

    @YuriiMotov
    Author

    I've just tested it with 1.63.0-alpha-2026-08-06 and it still fails:

    $ npm i -D @playwright/test@next
    
    changed 3 packages, and audited 4 packages in 35s
    found 0 vulnerabilities
    
    $ npx playwright install
    
    Downloading Chrome for Testing 152.0.7977.8 (playwright chromium v1237) from https://cdn.playwright.dev/builds/cft/152.0.7977.8/linux64/chrome-linux64.zip
    184.7 MiB [====================] 100% 0.0s
    Chrome for Testing 152.0.7977.8 (playwright chromium v1237) downloaded to ...
    ...
    
    $ npx playwright --version
    
    Version 1.63.0-alpha-2026-08-06
    
    $ npx playwright test --repeat-each=25 --workers=16
    
    ...
      2 failed
        [chromium] › tests/new-tab.spec.ts:3:5 › middle click opens a new tab ──────────────────────────
        [chromium] › tests/new-tab.spec.ts:3:5 › middle click opens a new tab ──────────────────────────
      48 passed (33.5s)
    
    
    npx envinfo --preset playwright
      System:
        OS: Linux 7.0 Ubuntu 24.04.4 LTS 24.04.4 LTS (Noble Numbat)
        CPU: (14) x64 Intel(R) Core(TM) Ultra 5 125U
        Memory: 29.22 GB / 46.52 GB
        Container: Yes
      Binaries:
        Node: 22.23.0 - /run/user/1000/fnm_multishells/308728_1786008511663/bin/node
        npm: 10.9.8 - /run/user/1000/fnm_multishells/308728_1786008511663/bin/npm
        bun: 1.3.8 - /home/yurii/.bun/bin/bun
      IDEs:
        VSCode: 1.124.2 - /usr/bin/code
      Languages:
        Bash: 5.2.21 - /usr/bin/bash
      npmPackages:
        @playwright/test: ^1.63.0-alpha-2026-08-06 => 1.63.0-alpha-2026-08-06 
    
  4. github-actions commented on Aug 6, 2026

    @github-actions
    Contributor

    Hi, I'm the Playwright bot and I took another look — thanks for coming back with the @next numbers.

    This is a live regression, still present on tip-of-tree. I can confirm it, and I'm correcting the earlier triage on this thread: it is not fixed by the newer Chromium roll. Clean on 1.61.1, broken from 1.62.0 onward, and still broken on 1.63.0-alpha with chromium-1237.

    What I ran (your MRE, unchanged, on a 4-core box — heavy oversubscription at 16 workers)
    @playwright/test Chromium Result
    1.61.1 headless-shell v1228 0 / 300 failed (3× --repeat-each=50)
    1.62.1 headless-shell v1234 2 / 50 failed (--repeat-each=25)
    1.63.0-alpha (@next) headless-shell v1237 8 / 300 failed (3× --repeat-each=50)

    Failure is the one you reported — browserContext.waitForEvent('page') / waitForURL hangs until the 30s deadline; the background tab either never opens or never navigates. It's timing-dependent, so a single 50-test run often passes clean — it takes repetition under load to surface, which is why the earlier "0 failures on ToT" run was misleading.

    Command: PLAYWRIGHT_HTML_OPEN=never npx playwright test --repeat-each=50 --workers=16

    Condensed self-contained repro

    Same trigger as your MRE, boiled down to our fixtures (still needs load/parallelism to surface — one run passes):

    test('middle click opens a background tab', {
      annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/42142' }
    }, async ({ page, context, server }) => {
      server.setRoute('/', (req, res) => {
        res.writeHead(200, { 'content-type': 'text/html' });
        res.end('<a href="/target.html">Open target</a>');
      });
      server.setRoute('/target.html', (req, res) => {
        res.writeHead(200, { 'content-type': 'text/html' });
        res.end('<h1>Target</h1>');
      });
      await page.goto(server.PREFIX + '/');
      const popupPromise = context.waitForEvent('page');
      await page.getByRole('link', { name: 'Open target' }).click({ button: 'middle' });
      await (await popupPromise).waitForURL('**/target.html');
    });

    Since 1.61.1 never fails across the full range you tried and the break lands squarely on 1.62.0, this looks like a real regression on our side that the Chromium update didn't touch. I'm a first pass, so I've left the root cause for a maintainer rather than guessing — but the boundary is clear and the repro is solid.

    Triaged by the Playwright bot.

    Triaged by the Playwright bot - agent run

  5. alirezaedalat commented on Aug 6, 2026

    @alirezaedalat

    Hi, I’d like to investigate this regression.

    My plan is to reproduce it using the provided repository, compare the behavior
    between 1.61.1 and 1.62.x, and identify whether the failure is related to input
    dispatch, modifier handling, or new-page event synchronization.

    If I can isolate the regression, I’ll add a focused regression test and propose
    a minimal fix.

    Please let me know if someone is already working on this.

    Thanks.

  6. Skn0tt commented on Aug 7, 2026

    @Skn0tt
    Contributor
  7. daddyman commented on Sep 1, 2026

    @daddyman

    We have had this problem intermittently for a long time, before 1.60, using a control-click to open a new tab. Random failures to navigate occurred a few times a week.

    The change we made was to not use the headless shell. In playwright.config.ts we added this to all the projects:

    channel: 'chromium', // full chromium, not the headless shell

    Since then, we have not had a new tab failure for over a month.

  8. 6 remaining items

  9. FGnosis commented on Oct 14, 2026

    @FGnosis

    Additional independent data point for the stranded-tab / missing page event symptom described above. We are not claiming a confirmed root cause or that every failure in this issue has the same cause.

    Versions and environment

    • playwright and playwright-core 1.62.1, Chromium headless shell 151.0.7922.34, revision 1234, default chromium.launch({ headless: true }) in the failing CI run.
    • Failing run: GitHub-hosted Ubuntu 24.04, Linux x86_64, Node 22.23.3, npm 10.9.9. The Node script runs scenarios sequentially, without Playwright Test workers or retries; preceding scenarios had already run in the same browser.
    • Comparison: five fresh local Node processes passed on Linux ARM64, using the same SDK versions and headless-shell revision via an explicit executablePath, with CPU quota 1 and memory limit 1 GiB. The first CI iteration failed; we stopped there. Different architecture/resource/timing conditions, so this is not a controlled local-vs-CI comparison or a statistical failure rate.
    • We have not tested the latest release for this report. No version boundary, regression range, upgrade fix, or relation to the Chromium CL mentioned earlier is claimed.

    Observed API sequence (excerpt of the existing application test, not a standalone minimal reproduction):

    // A same-origin dashboard with a stretched row link, after several normal
    // in-page navigation/back cycles. The destination is fulfilled by context.route.
    await context.route(destination, route => route.fulfill({
      contentType: 'text/html', body: '<!doctype html><title>Target</title>'
    }));
    const opened = context.waitForEvent('page');
    opened.catch(() => {}); // preserve the original await while observing errors
    await page.locator(rowHeaderSelector).first().click({
      force: true, modifiers: ['Control']
    });
    const popup = await opened; // timed out after 30 s
    await popup.waitForLoadState();

    rowHeaderSelector and destination are application-specific variables; no claim that this excerpt alone reproduces the failure. We are adding diagnostic evidence to this existing issue with a public MRE, rather than filing a new bug without a validated standalone reproduction.

    What the failing trace establishes

    • Control-down and mouse moved/pressed/released carried modifiers: 2; all input commands were ACKed. Control-up followed them.
    • A second Target.attachedToTarget, type page, arrived in the same browser context, initially waitingForDebugger: true.
    • Its Page.enable, Page.setLifecycleEventsEnabled, Runtime.enable, Target.setAutoAttach, and Runtime.runIfWaitingForDebugger commands were ACKed without errors. DOMContentLoaded, load, and frameStoppedLoading arrived on its session.
    • A post-timeout, protected browser.newBrowserCDPSession() / Target.getTargets check confirmed both targets still attached, the opener at its dashboard URL and the second target at the exact expected destination. That check was not installed before the click.
    • The same context still exposed only the opener via context.pages(). A Node-side context.on('page') listener had recorded zero new-page events.
    • Across the received protocol messages on the connection, there was no Page.frameNavigated for the popup and no lifecycle init during the destination load. The trace ring had 0 dropped records and 0 parse failures. In all five successful local captures, init and frameNavigated preceded the Node page event (5–10 ms after target attachment, or 6–11 ms after the final mouse-input send).

    Capture limitations
    The capture was a temporary Node-side hook of the SDK's existing protocol logger, with no listeners, observers, or injected code added to the page. It retained only a whitelist of input/target/page lifecycle metadata and ACKs; raw network, headers, cookies, evaluation payloads, and document content were discarded. Enabling protocol serialization and parsing still changes Node-side timing, so successful runs are only non-reproductions. The initial empty target URL is represented below as [invalid-url]; that is our sanitizer label for an empty string, not a Chromium error. The early lifecycle group at browser timestamp 541.569 (only its DOMContentLoaded is shown) also appears in successful runs; the later 541.612 DOMContentLoaded/load follows debugger release. Loader IDs were not retained, so those groups alone do not identify a particular new loader.

    Our whitelist did not retain Page.getFrameTree or its response, or all other initialization-command ACKs. In the 1.62.1 SDK, page initialization waits for _firstNonInitialNavigationCommittedPromise, resolved either when the initial frame-tree URL is neither empty nor ":", or when a non-initial frameNavigated arrives. The trace is compatible with that wait remaining pending, but another initialization promise could also be pending. We cannot distinguish those possibilities or explain why the commit events are missing from this capture. This is a localization of the failure, not proof of the precise blocking await or upstream cause.

    Sanitized failing trace (relative times; URLs and opaque IDs replaced consistently)
    {
      "note": "Independent failing occurrence; opaque IDs and URLs consistently replaced; times relative to Control-down; omitted events are not represented as absent. The earlier 5 successful local traces are not pasted here.",
      "events": [
        {
          "relativeMs": 0,
          "kind": "cdp",
          "direction": "send",
          "sessionId": "opener-session",
          "method": "Input.dispatchKeyEvent",
          "params": {
            "modifiers": 2,
            "autoRepeat": false,
            "isKeypad": false,
            "type": "rawKeyDown",
            "key": "Control",
            "code": "ControlLeft",
            "windowsVirtualKeyCode": 17
          }
        },
        {
          "relativeMs": 0,
          "kind": "ack",
          "sessionId": "opener-session",
          "method": "Input.dispatchKeyEvent",
          "result": {}
        },
        {
          "relativeMs": 0,
          "kind": "cdp",
          "direction": "send",
          "sessionId": "opener-session",
          "method": "Input.dispatchMouseEvent",
          "params": {
            "modifiers": 2,
            "x": 559.37,
            "y": 261.25,
            "buttons": 0,
            "type": "mouseMoved",
            "button": "none"
          }
        },
        {
          "relativeMs": 0,
          "kind": "cdp",
          "direction": "send",
          "sessionId": "opener-session",
          "method": "Input.dispatchMouseEvent",
          "params": {
            "modifiers": 2,
            "x": 559.37,
            "y": 261.25,
            "buttons": 1,
            "clickCount": 1,
            "type": "mousePressed",
            "button": "left"
          }
        },
        {
          "relativeMs": 1,
          "kind": "cdp",
          "direction": "send",
          "sessionId": "opener-session",
          "method": "Input.dispatchMouseEvent",
          "params": {
            "modifiers": 2,
            "x": 559.37,
            "y": 261.25,
            "buttons": 0,
            "clickCount": 1,
            "type": "mouseReleased",
            "button": "left"
          }
        },
        {
          "relativeMs": 1,
          "kind": "ack",
          "sessionId": "opener-session",
          "method": "Input.dispatchMouseEvent",
          "result": {}
        },
        {
          "relativeMs": 2,
          "kind": "ack",
          "sessionId": "opener-session",
          "method": "Input.dispatchMouseEvent",
          "result": {}
        },
        {
          "relativeMs": 3,
          "kind": "ack",
          "sessionId": "opener-session",
          "method": "Input.dispatchMouseEvent",
          "result": {}
        },
        {
          "relativeMs": 3,
          "kind": "cdp",
          "direction": "send",
          "sessionId": "opener-session",
          "method": "Input.dispatchKeyEvent",
          "params": {
            "modifiers": 0,
            "type": "keyUp",
            "key": "Control",
            "code": "ControlLeft",
            "windowsVirtualKeyCode": 17
          }
        },
        {
          "relativeMs": 4,
          "kind": "cdp",
          "direction": "recv",
          "method": "Target.attachedToTarget",
          "params": {
            "sessionId": "popup-session",
            "waitingForDebugger": true,
            "targetInfo": {
              "targetId": "popup-target",
              "browserContextId": "context-A",
              "attached": true,
              "canAccessOpener": false,
              "type": "page",
              "url": "[invalid-url]"
            }
          }
        },
        {
          "relativeMs": 11,
          "kind": "ack",
          "sessionId": "opener-session",
          "method": "Input.dispatchKeyEvent",
          "result": {}
        },
        {
          "relativeMs": 11,
          "kind": "cdp",
          "direction": "send",
          "sessionId": "popup-session",
          "method": "Page.enable",
          "params": {}
        },
        {
          "relativeMs": 11,
          "kind": "cdp",
          "direction": "send",
          "sessionId": "popup-session",
          "method": "Page.setLifecycleEventsEnabled",
          "params": {
            "enabled": true
          }
        },
        {
          "relativeMs": 11,
          "kind": "cdp",
          "direction": "send",
          "sessionId": "popup-session",
          "method": "Runtime.enable",
          "params": {}
        },
        {
          "relativeMs": 12,
          "kind": "cdp",
          "direction": "send",
          "sessionId": "popup-session",
          "method": "Target.setAutoAttach",
          "params": {
            "flatten": true,
            "autoAttach": true,
            "waitForDebuggerOnStart": true
          }
        },
        {
          "relativeMs": 12,
          "kind": "cdp",
          "direction": "send",
          "sessionId": "popup-session",
          "method": "Runtime.runIfWaitingForDebugger",
          "params": {}
        },
        {
          "relativeMs": 24,
          "kind": "ack",
          "sessionId": "popup-session",
          "method": "Target.setAutoAttach",
          "result": {}
        },
        {
          "relativeMs": 31,
          "kind": "ack",
          "sessionId": "popup-session",
          "method": "Page.enable",
          "result": {}
        },
        {
          "relativeMs": 31,
          "kind": "cdp",
          "direction": "recv",
          "sessionId": "popup-session",
          "method": "Page.lifecycleEvent",
          "params": {
            "frameId": "popup-target",
            "timestamp": 541.569261,
            "name": "DOMContentLoaded"
          }
        },
        {
          "relativeMs": 31,
          "kind": "ack",
          "sessionId": "popup-session",
          "method": "Page.setLifecycleEventsEnabled",
          "result": {}
        },
        {
          "relativeMs": 32,
          "kind": "ack",
          "sessionId": "popup-session",
          "method": "Runtime.enable",
          "result": {}
        },
        {
          "relativeMs": 50,
          "kind": "ack",
          "sessionId": "popup-session",
          "method": "Runtime.runIfWaitingForDebugger",
          "result": {}
        },
        {
          "relativeMs": 54,
          "kind": "cdp",
          "direction": "recv",
          "sessionId": "popup-session",
          "method": "Page.lifecycleEvent",
          "params": {
            "frameId": "popup-target",
            "timestamp": 541.612229,
            "name": "DOMContentLoaded"
          }
        },
        {
          "relativeMs": 55,
          "kind": "cdp",
          "direction": "recv",
          "sessionId": "popup-session",
          "method": "Page.lifecycleEvent",
          "params": {
            "frameId": "popup-target",
            "timestamp": 541.612352,
            "name": "load"
          }
        },
        {
          "relativeMs": 55,
          "kind": "cdp",
          "direction": "recv",
          "sessionId": "popup-session",
          "method": "Page.frameStoppedLoading",
          "params": {
            "frameId": "popup-target"
          }
        }
      ],
      "afterTimeout": {
        "targets": [
          {
            "type": "page",
            "targetId": "opener-target",
            "browserContextId": "context-A",
            "attached": true,
            "url": "https://example.test/dashboard/"
          },
          {
            "type": "page",
            "targetId": "popup-target",
            "browserContextId": "context-A",
            "attached": true,
            "url": "https://example.test/target/"
          }
        ],
        "contextPages": [
          "https://example.test/dashboard/"
        ],
        "nodePageEvents": 0,
        "traceDropped": 0,
        "traceParseFailures": 0,
        "missingForPopup": [
          "Page.frameNavigated",
          "Page.lifecycleEvent name=init"
        ]
      }
    }

    No private application URL, repository link, filesystem path, original target/context/session identifier, credential, or full raw protocol log is included. We have not used target polling/forced closure, DOM default-action cancellation, a different browser channel, or another acceptance mechanism as a replacement for the original test.

  10. dgozman commented on Oct 27, 2026

    @dgozman
    Collaborator

    🛑 Blocked on upstream issue https://issues.chromium.org/issues/543471700

  11. pavelfeldman commented on Nov 2, 2026

    @pavelfeldman
    Member

    This is fixed upstream (Chromium) on Oct 27th. Will likely make it only into v1.66.

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

Metadata

Metadata

Labels

browser-chromiumBlocked on upstream chromium issue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions