How to fix Playwright 'Cannot read properties of undefined' with connectOverCDP newPage()
PlaywrightWhen you call browser.newPage() on a browser connected via connectOverCDP(), Playwright throws "Cannot read properties of undefined (reading '_page')" because the browser's default context is not owned by Playwright. The connectOverCDP() method attaches your Playwright instance to an existing browser process (like Chrome or Edge already running), but the default context of that browser was created outside of Playwright's control. Calling newPage() on the browser object tries to use an internal _page property that doesn't exist in that unowned context, causing the error.
Understanding connectOverCDP
connectOverCDP() lets you connect to a browser that's already running and managed by something else — a test runner, a production service, or another process. You're attaching Playwright to that browser rather than launching it yourself:
// This connects to an EXISTING browser, not launching one
const browser = await playwright.chromium.connectOverCDP('http://localhost:9222');
The key insight: the browser's default context was created before Playwright connected, so Playwright doesn't "own" it. Calling newPage() expects an owned context.
The problem: calling newPage() on the browser
This fails with the _page error:
const browser = await playwright.chromium.connectOverCDP('http://localhost:9222');
const page = await browser.newPage(); // ❌ Error: Cannot read properties of undefined (reading '_page')
The fix: use the existing context
Instead, access the browser's existing context via browser.contexts()[0]:
const browser = await playwright.chromium.connectOverCDP('http://localhost:9222');
const context = browser.contexts()[0]; // Get the default context
const page = await context.newPage(); // ✓ Works
If you need a new isolated context (separate cookies, cache, etc.), create one explicitly:
const browser = await playwright.chromium.connectOverCDP('http://localhost:9222');
const newContext = await browser.createContext(); // Create a new owned context
const page = await newContext.newPage(); // ✓ Works
Why it works
When you connect via connectOverCDP(), Playwright sees the browser's existing contexts but doesn't own them — they were created by the browser itself. Calling newPage() on the browser object looks for an owned default context to attach the page to, but that context has an unset _page property because it's external. Calling newPage() on a context object you own (or explicitly created) works because Playwright manages its lifecycle.
Version skew
Another common cause: your Playwright client version differs from the Chrome/browser version at the CDP endpoint. A Playwright v1.40 client connecting to a Chrome 120 instance built for Playwright v1.45 can trigger version-incompatible internal structures. Always keep your Playwright version in sync with the target browser version, or use the same Playwright for launching and connecting.
// ✓ Best practice: launch and connect with the same Playwright
const browser = await chromium.launch({ args: ['--remote-debugging-port=9222'] });
const connected = await chromium.connectOverCDP('http://localhost:9222');
Tips
- Use browser.contexts()[0].newPage() instead of browser.newPage() when connecting via connectOverCDP().
- If you need isolation (separate cookies/cache), call browser.createContext() to create a new owned context, then use newContext.newPage().
- Avoid connecting to browsers launched outside your Playwright version — CDP endpoints are version-sensitive.
- For the general version of this error unrelated to CDP, see Cannot read properties of undefined (reading _page).