Version
@playwright/cli 0.1.15 (bundled playwright-core 1.62.0-alpha-2026-06-29)
Steps to reproduce
Environment: Windows 11, Chrome 150.0.7871.101, attaching to a long-running real Chrome profile.
- Run Chrome with
--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's Memory Saver discards/freezes one of the idle background tabs. (Likely forcible on demand via
chrome://discards → "Urgent discard", though my occurrence was organic.)
playwright-cli attach --cdp=http://localhost:9222 → succeeds.
- Run any command:
playwright-cli tab-list, eval "location.href", snapshot, …
Expected behavior
Commands work, with the discarded tab either listed as-is (title from target info) or marked unresponsive.
Actual behavior
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.
Diagnosis
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.
Suggested fix
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.
Workaround
Close and recreate the discarded tab via a browser-level CDP session (Target.closeTarget + Target.createTarget) — the CLI heals instantly.
Version
@playwright/cli 0.1.15 (bundled playwright-core 1.62.0-alpha-2026-06-29)
Steps to reproduce
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, …Expected behavior
Commands work, with the discarded tab either listed as-is (title from target info) or marked unresponsive.
Actual behavior
Every command fails after 30s with:
The daemon is fully wedged — even commands that don't touch the bad tab (e.g.
evalon the current tab) fail, because every command's response renders the "Open tabs" / "Page" header sections.Diagnosis
The discarded tab's renderer never answers renderer-bound CDP calls. Since #41128 (fix for #41093),
connectOverCDPno longer hangs at connect time on such a tab — it reports the page "as-is". The result is a half-initializedPagewhoseurl()returns""and whosetitle()/evaluate()never resolve (they don't recover even on fresh connections, or after the renderer is revived viaTarget.activateTarget/Page.navigate; only closing the tab clears it).The tools backend then hangs on it for every command:
Tab.headerSnapshot()does(
packages/playwright-core/src/tools/backend/tab.ts, visible atlib/coreBundle.js:63606in the published bundle) and response rendering awaitsheaderSnapshot()for all tabs on every command. One zombie tab ⇒ every command times out.Probing each page with
Promise.race([page.title(), timeout])over a rawconnectOverCDPconnection pinpoints the culprit: five healthy tabs answer in ms; the discarded one (emptyurl()) hangs on bothtitle()andevaluate('1+1'), while browser-side CDP on the same target (Target.getTargetInfo) still answers fine and even knows the real title/URL.Suggested fix
Guard the per-tab header with a small timeout and/or
.catch()(likecollectConsoleMessagesalready does), falling back toTargetInfo.title/urlfrom 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.Workaround
Close and recreate the discarded tab via a browser-level CDP session (
Target.closeTarget+Target.createTarget) — the CLI heals instantly.