microsoft/playwright · error · Error

JSHandle is not a DOM node handle

Error message

JSHandle is not a DOM node handle

What it means

Thrown by the WebDriver BiDi execution context when an internal operation needs a remote object reference from a JSHandle, but the handle's `_objectId` is falsy. A primitive JSHandle (string, number, boolean, undefined, null) has no backing remote object in BiDi, so it cannot be used where a node/window handle reference is required.

Source

Thrown at packages/playwright-core/src/server/bidi/bidiExecutionContext.ts:167

  }

  async remoteObjectForNodeId(context: dom.FrameExecutionContext, nodeId: bidi.Script.SharedReference): Promise<dom.ElementHandle> {
    const result = await this._remoteValueForReference(nodeId, true);
    if (!('handle' in result))
      throw new Error('Can\'t get remote object for nodeId');
    return createHandle(context, result) as dom.ElementHandle;
  }

  async contentFrameIdForFrame(handle: dom.ElementHandle) {
    const contentWindow = await this._rawCallFunction('e => e.contentWindow', [{ handle: handle._objectId }]);
    if (contentWindow?.type === 'window')
      return contentWindow.value.context;
    return null;
  }

  async frameIdForWindowHandle(handle: js.JSHandle): Promise<string | null> {
    if (!handle._objectId)
      throw new Error('JSHandle is not a DOM node handle');
    const contentWindow = await this._remoteValueForReference({ handle: handle._objectId });
    if (contentWindow.type === 'window')
      return contentWindow.value.context;
    return null;
  }

  private async _remoteValueForReference(reference: bidi.Script.RemoteReference, createHandle?: boolean) {
    return await this._rawCallFunction('e => e', [reference], createHandle);
  }

  private async _rawCallFunction(functionDeclaration: string, args: bidi.Script.LocalValue[], createHandle?: boolean, awaitPromise = true): Promise<bidi.Script.RemoteValue> {
    const response = await this._session.send('script.callFunction', {
      functionDeclaration,
      target: this._target,
      arguments: args,
      // "Root" is necessary for the handle to be returned.
      resultOwnership: createHandle ? bidi.Script.ResultOwnership.Root : bidi.Script.ResultOwnership.None,
      serializationOptions: { maxObjectDepth: 0, maxDomDepth: 0 },

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Obtain handles from expressions that return objects/windows, e.g. `page.evaluateHandle(() => window)`, not primitives
  2. If you hit this through a public API, file a Playwright issue with a repro — it typically signals an internal invariant violation
  3. Avoid passing arbitrary `evaluateHandle` results into frame/element APIs; re-query the element with a Locator

Example fix

// before
const h = await page.evaluateHandle(() => 'foo');
// ... passed into frame logic

// after
const h = await page.evaluateHandle(() => window);
// or re-query the node directly
const loc = page.locator('iframe');
Defensive patterns

Strategy: try-catch

Validate before calling

// _objectId is internal; guard at the public boundary by ensuring
// the handle comes from an object-returning expression.
const h = await page.evaluateHandle(() => window);
// Re-query nodes via Locators rather than reusing arbitrary handles.

Type guard

// No public _objectId; approximate by checking the handle is non-primitive.
function isObjectHandle(h: JSHandle): boolean {
  // Primitives report these types; objects/nodes report 'object'/'function'/'node'.
  const t = (h as any)._objectType ?? 'object';
  return t === 'object' || t === 'function' || t === 'node';
}

Try / catch

try {
  // operation that needs a node/window handle
} catch (e) {
  if (e instanceof Error && /not a DOM node handle/.test(e.message)) {
    // re-acquire a valid handle or rethrow with context
  }
  throw e;
}

Prevention

When it happens

Trigger: Internal call to `frameIdForWindowHandle(handle)` in bidiExecutionContext.ts:167 where `handle._objectId` is undefined. Reached when BiDi frame/window introspection is given a handle produced by `page.evaluateHandle(() => somePrimitive)` instead of an object/window.

Common situations: Rare for end users. Surfaces in Firefox-over-BiDi when a primitive-valued JSHandle leaks into frame-resolution logic, or when internal helpers are reused with handles of the wrong shape. Usually indicates a Playwright internal bug rather than a user mistake.

Related errors


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