microsoft/playwright · warning · Error
This frame does not have a separate CDP session, it is a par
Error message
This frame does not have a separate CDP session, it is a part of the parent frame's session
What it means
Thrown in CRBrowserContext.newCDPSession when called with a Frame whose _id has no entry in the CRPage _sessions map. Out-of-process frames (OOPIFs) get their own CDP target/session; same-process frames share the parent page's session and therefore cannot yield a dedicated CDPSession.
Source
Thrown at packages/playwright-core/src/server/chromium/crBrowser.ts:606
// cancellation (finished, cancelled, etc.) or the guid is invalid at all.
await this._browser._session.send('Browser.cancelDownload', {
guid: guid,
browserContextId: this._browserContextId,
});
}
serviceWorkers(): CRServiceWorker[] {
return Array.from(this._browser._serviceWorkers.values()).filter(serviceWorker => serviceWorker.browserContext === this);
}
async newCDPSession(page: Page | Frame): Promise<CDPSession> {
let targetId: string | null = null;
if (page instanceof Page) {
targetId = (page.delegate as CRPage)._targetId;
} else if (page instanceof Frame) {
const session = (page._page.delegate as CRPage)._sessions.get(page._id);
if (!session)
throw new Error(`This frame does not have a separate CDP session, it is a part of the parent frame's session`);
targetId = session._targetId;
} else {
throw new Error('page: expected Page or Frame');
}
const rootSession = await this._browser._clientRootSession();
return rootSession.attachToTarget(targetId);
}
}
export function shouldProxyLoopback(bypass: string | undefined) {
if (process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK)
return false;
const hosts = (bypass || '').split(',').map(s => s.trim());
const shouldBypassSomeLoopback = ['localhost', '127.0.0.1', '::1', '[::]', '[::1]', '<loopback>', '<-loopback>'].some(host => hosts.includes(host));
return !shouldBypassSomeLoopback;
}
View on GitHub (pinned to c8fc3bf8d3)
Solutions
- Call newCDPSession on the Page instead (frames in the same process share the page's session), then target the frame via its target/frame ID.
- If you truly need a per-frame session, ensure the frame is out-of-process (cross-site/origin-isolated) — but this is browser-dependent.
- Use Playwright's frame APIs (frame.evaluate, frame.locator) instead of raw CDP for same-process frames.
Example fix
// before
const sess = await context.newCDPSession(sameOriginFrame);
// after
const sess = await context.newCDPSession(sameOriginFrame._page);
// or avoid CDP:
await sameOriginFrame.evaluate(() => { /* ... */ }); Defensive patterns
Strategy: type-guard
Validate before calling
async function safeNewCDPSession(context: any, target: any) {
if (target && target._id) {
const page = target._page;
const hasSession = page && page.delegate && page.delegate._sessions && page.delegate._sessions.has(target._id);
if (!hasSession) return context.newCDPSession(page); // fall back to the page session
}
return context.newCDPSession(target);
} Type guard
function frameHasOwnCDPSession(frame: any): boolean {
const page = frame?._page;
return !!(page?.delegate?._sessions?.has(frame._id));
} Try / catch
try {
session = await context.newCDPSession(frame);
} catch (e) {
if (/does not have a separate CDP session/.test(String(e.message))) {
session = await context.newCDPSession(frame._page);
} else throw e;
} Prevention
- Prefer Playwright frame APIs over raw CDP for same-process iframes.
- Only request a frame-level CDPSession for out-of-process (cross-site) frames.
- Fall back to the page's CDPSession when a frame shares its parent's session.
When it happens
Trigger: await context.newCDPSession(someFrame) where someFrame is a same-process iframe (not an OOPIF), so no separate CDP session was attached to it.
Common situations: Calling newCDPSession on a frame to run CDP commands, but the iframe is same-origin (same process) and the browser never created a separate target for it. Code that works for cross-origin iframes fails for same-origin ones.
Related errors
- Could not connect to ${channel}.\n${remoteDebuggingHint(chan
- Playwright manages remote debugging connection itself.
- Connecting to ${channel} by channel name is not supported on
- Could not connect to ${channel}: DevToolsActivePort file not
- Could not connect to ${channel}: invalid DevToolsActivePort
AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12).
Data as JSON: /api/errors/328a2cacc5b44ab1.
Report an issue: GitHub.