← Back to microsoft/playwright
microsoft / playwright · Issue No. 41714
@playwright/cli 0.1.15 (bundled playwright-core 1.62.0-alpha-2026-06-29)
Environment: Windows 11, Chrome 150.0.7871.101, attaching to a long-running real Chrome profile.
--remote-debugging-port=9222 --user-data-dir=<dedicated profile> and use it normally for a few days with several tabs open (Stripe/Azure/Vercel dashboards in my case).chrome://discards → "Urgent discard", though my occurrence was organic.)playwright-cli attach --cdp=http://localhost:9222 → succeeds.playwright-cli tab-list, eval "location.href", snapshot, …Commands work, with the discarded tab either listed as-is (title from target info) or marked unresponsive.
Every command fails after 30s with:
### Error
TimeoutError: Timeout 30000ms exceeded.
The daemon is fully wedged — even commands that don't touch the bad tab (e.g. eval on the current tab) fail, because every command's response renders the "Open tabs" / "Page" header sections.
The discarded tab's renderer never answers renderer-bound CDP calls. Since #41128 (fix for #41093), connectOverCDP no longer hangs at connect time on such a tab — it reports the page "as-is". The result is a half-initialized Page whose url() returns "" and whose title() / evaluate() never resolve (they don't recover even on fresh connections, or after the renderer is revived via Target.activateTarget / Page.navigate; only closing the tab clears it).
The tools backend then hangs on it for every command: Tab.headerSnapshot() does
await this._raceAgainstModalStates(async () => {
title = await this.page.title(); // no timeout, no catch
});
(packages/playwright-core/src/tools/backend/tab.ts, visible at lib/coreBundle.js:63606 in the published bundle) and response rendering awaits headerSnapshot() for all tabs on every command. One zombie tab ⇒ every command times out.
Probing each page with Promise.race([page.title(), timeout]) over a raw connectOverCDP connection pinpoints the culprit: five healthy tabs answer in ms; the discarded one (empty url()) hangs on both title() and evaluate('1+1'), while browser-side CDP on the same target (Target.getTargetInfo) still answers fine and even knows the real title/URL.
Guard the per-tab header with a small timeout and/or .catch() (like collectConsoleMessages already does), falling back to TargetInfo.title/url from the browser session, and render the tab as e.g. (unresponsive). Related report of the same underlying condition in another CDP-attaching agent tool: vercel-labs/agent-browser#1036.
Close and recreate the discarded tab via a browser-level CDP session (Target.closeTarget + Target.createTarget) — the CLI heals instantly.
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.