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

  1. 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.
  2. If you truly need a per-frame session, ensure the frame is out-of-process (cross-site/origin-isolated) — but this is browser-dependent.
  3. 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

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


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/328a2cacc5b44ab1. Report an issue: GitHub.