microsoft/playwright · error · Error

page: expected Page or Frame

Error message

page: expected Page or Frame

What it means

Thrown by BrowserContext.newCDPSession(page) when the argument is neither a Page nor a Frame instance. Because the parameter type is a pseudo-union (Page|Frame) that the generic channelOwner validator cannot describe, this method does explicit instanceof checks and throws with a clear message naming the param.

Source

Thrown at packages/playwright-core/src/client/browserContext.ts:504

  }

  async setStorageState(storageState: string | SetStorageState): Promise<void> {
    const state = await prepareStorageState(storageState);
    await this._channel.setStorageState({ storageState: state }, kNoTimeout);
  }

  backgroundPages(): Page[] {
    return [];
  }

  serviceWorkers(): Worker[] {
    return [...this._serviceWorkers];
  }

  async newCDPSession(page: Page | Frame): Promise<api.CDPSession> {
    // channelOwner.ts's validation messages don't handle the pseudo-union type, so we're explicit here
    if (!(page instanceof Page) && !(page instanceof Frame))
      throw new Error('page: expected Page or Frame');
    const result = await this._channel.newCDPSession(page instanceof Page ? { page: page._channel } : { frame: page._channel }, kNoTimeout);
    return CDPSession.from(result.session);
  }

  _onClose() {
    this._closingStatus = 'closed';
    this._browser?._contexts.delete(this);
    this._browser?._browserType._contexts.delete(this);
    this._browser?._browserType._playwright.selectors._contextsForSelectors.delete(this);
    this._disposeHarRouters();
    this.tracing._resetStackCounter();
    this.request.tracing._resetStackCounter();
    this.emit(Events.BrowserContext.Close, this);
  }

  async [Symbol.asyncDispose]() {
    await this.close();
  }

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Pass a Page or Frame from the same connection: `await context.newCDPSession(page)`.
  2. For worker targets use the appropriate chromium worker API, not newCDPSession.
  3. Ensure the object originates from the same browser/connection instance.

Example fix

// before
await context.newCDPSession(worker);
// after
await context.newCDPSession(page);
Defensive patterns

Strategy: type-guard

Validate before calling

function isValidCdpTarget(t: unknown): boolean {
  return t instanceof Page || t instanceof Frame;
}
if (!isValidCdpTarget(target))
  throw new Error('newCDPSession expects a Page or Frame from the same connection.');

Type guard

function isPageOrFrame(t: unknown): t is Page | Frame {
  return t instanceof Page || t instanceof Frame;
}

Prevention

When it happens

Trigger: Passing a Worker, BrowserContext, JSHandle, ElementHandle, a raw channel object, or a value from a different Playwright instance into newCDPSession(). Both instanceof checks fail.

Common situations: Passing a worker reference when intending a page; mixing objects across two separate Playwright connections (each has its own class realm); passing a serialized/hand-off object.

Related errors


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