Skip to content

playwright-cli attach hangs forever: Tab.headerSnapshot awaits page.title() without a timeout on a half-initialised page #489

Open

Description

@cyppe

Version

@playwright/cli 0.1.22 with playwright-core 1.64.1 (also seen with 0.1.21 / playwright-core 1.64.0-alpha). Chromium 154 on Linux. Code unchanged on microsoft/playwright main at b4695e3eb2.

Steps to reproduce

  1. Start Chromium with --remote-debugging-port=9222 and use it normally, with many tabs (here 28). In our case a hidden tab that had been reloaded ended up in this state; we have no deterministic way to create it from scratch.
  2. Run playwright-cli -s=s attach --cdp=http://127.0.0.1:9222.

Expected

Attach completes, listing the tabs. A tab that cannot report its title is listed without one.

Actual

attach never returns. The session daemon stays registered, and every later command (even run-code 'async page => 1') queues behind it and hangs too, until the session is detached.

Analysis

Minimal probe (touches no tab):

const { chromium } = require('playwright-core');
(async () => {
  const browser = await chromium.connectOverCDP('http://127.0.0.1:9222');
  for (const page of browser.contexts()[0].pages()) {
    const outcome = await Promise.race([page.title().then(() => 'ok'), new Promise(r => setTimeout(r, 3000, 'HANGS'))]);
    if (outcome !== 'ok') console.log(outcome, JSON.stringify(page.url()));   // -> HANGS ""
  }
  process.exit(0);
})();

Workaround we use

We bound the call: 3 s for the first wait, then 100 ms per later snapshot while the same pending title() is still unresolved, and leave the title empty. With it, attach takes 3.7 s instead of hanging, later commands take 0.3 s, and the tab is listed as - 6: [](). Unpatched, the same browser times out at 60 s every time.

Suggestion

Bound title() in headerSnapshot() (a small change; PR to follow in microsoft/playwright). Separately, the frame never leaving the "commit"/url "" state while the renderer is loaded may be a bug in frame tracking after connectOverCDP; we can collect a pw:protocol log for that tab if it helps.

Activity

  1. dgozman commented on Oct 26, 2026

    @dgozman
    Contributor

    cyppe Any chance you can dump the contents of chrome://discards and chrome://process-internals (all three tabs there) next time this happens? We really need to figure out what's the state of the page that prevents attach from completing, to figure out a long-term solution.

  2. github-actions commented on Oct 31, 2026

    @github-actions
  3. github-actions commented on Oct 31, 2026

    @github-actions

    Hi, I'm the Playwright bot. I tried to reproduce this on Linux.

    I could not recreate the half-initialized frame, on current main or with playwright-core 1.64.1. With 28 tabs, background reloads and frozen tabs, attachment and later commands completed. A coverage test for attachment after a background-tab reload is proposed; it does not fix or simulate the reported missing execution context.

    One relevant detail: page.title() evaluates in Playwright's utility world, not the default world used by bare CDP Runtime.evaluate. A successful default-world evaluation does not establish that the utility-world context was initialized. The timeout patch in microsoft/playwright#43620 was closed without merging, so I have not repeated it.

    Could you share a sanitized DEBUG=pw:protocol,pw:channel log starting before the failing attach, together with the chrome://discards and chrome://process-internals dumps requested above? The missing piece is a way to create, or inspect initialization of, the affected frame.

    What I ran

    All runs were on Linux. Each probe used 28 tabs and checked all titles after CDP connection, with a five-second bound on title collection. Variations were ordinary loaded tabs, a frozen background tab, reload while frozen, thawing that tab, and 20 reload/connection races.

    CLI/core Browser Mode Result
    Upstream 1.65.0-next, a6fb02f5aa494764a21a637f2db6ea1031461935 Chromium 158.0.8127.2 Headless and headed All title probes completed
    Same upstream commit Chrome 156.0.8078.47 Headed All title probes completed, both with the original Playwright sessions attached and after detaching them
    CLI 0.1.22, pinned core 1.64.0-alpha-1790635538000, upstream e8149b8257d32dcf8f72573ecc43e72439da7080 Chromium 155.0.8059.12 Headed All title probes completed
    CLI 0.1.22, core 1.64.1, upstream cfe490d941f992655948eee8fd4fd94e1c4234bf Chrome 156.0.8078.47 Headed All title probes completed

    For upstream Chrome, the pinned core, and core 1.64.1, I also disabled the original session's focus emulation and confirmed document.hidden === true on the background tab before testing. In the four non-race states, actual CLI attach, tab-list, and run-code 'async page => 1' all completed and returned the expected titles/result. No probe produced the reported empty-URL frame.

    The reporter's Chromium 154 and long-running browser profile were not available. These results do not establish that the bug is fixed.

    The existing attach via cdp test now includes the background reload and checks attachment metadata, a snapshot, and a later run-code result. cli-cdp.spec.ts and cli-endpoint.spec.ts passed across the Chrome, Chromium, Firefox, and WebKit projects: 42 passed, 14 skipped. CDP-only cases are skipped for Firefox and WebKit. Build and the full npm run flint check passed.

    Condensed scenario covered by the proposal

    Using the repository's cdpServer, cli, and server fixtures:

    const context = await cdpServer.start();
    const [page] = context.pages();
    await page.goto(server.HELLO_WORLD);
    const backgroundPage = await context.newPage();
    await backgroundPage.goto(server.PREFIX + '/title.html');
    await page.bringToFront();
    await backgroundPage.reload();
    
    const attached = await cli('attach', `--cdp=${cdpServer.endpoint}`);
    expect(attached.exitCode).toBe(0);
    expect(attached.output).toContain(`[Woof-Woof](${server.PREFIX}/title.html)`);
    const result = await cli('run-code', 'async page => 1');
    expect(result.output).toContain('### Result\n1');

    This passes today. It covers the background-reload setup, not the unknown trigger that leaves a frame without an execution context.

    Draft PR: microsoft/playwright#43816

    Triaged on Linux by the Playwright bot - agent run

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions