microsoft/playwright · error · Error

Unable to adopt element handle from a different document

Error message

Unable to adopt element handle from a different document

What it means

Thrown by CRFrameSession._adoptBackendNodeId (crPage.ts:1170) when DOM.resolveNode returns nothing or a null subtype for the given backendNodeId + executionContextId. The node cannot be resolved into the destination context — typically because it lives in a different document that the target context cannot reach.

Source

Thrown at packages/playwright-core/src/server/chromium/crPage.ts:1170

      { x: quad[4] + position.x, y: quad[5] + position.y },
      { x: quad[6] + position.x, y: quad[7] + position.y }
    ]);
  }

  async _adoptElementHandle<T extends Node>(handle: dom.ElementHandle<T>, to: dom.FrameExecutionContext): Promise<dom.ElementHandle<T>> {
    const nodeInfo = await this._client.send('DOM.describeNode', {
      objectId: handle._objectId,
    });
    return this._adoptBackendNodeId(nodeInfo.node.backendNodeId, to) as Promise<dom.ElementHandle<T>>;
  }

  async _adoptBackendNodeId(backendNodeId: Protocol.DOM.BackendNodeId, to: dom.FrameExecutionContext): Promise<dom.ElementHandle> {
    const result = await this._client._sendMayFail('DOM.resolveNode', {
      backendNodeId,
      executionContextId: (to.delegate as CRExecutionContext)._contextId,
    });
    if (!result || result.object.subtype === 'null')
      throw new Error(dom.kUnableToAdoptErrorMessage);
    return createHandle(to, result.object).asElement()!;
  }
}

async function emulateLocale(session: CRSession, locale: string) {
  try {
    await session.send('Emulation.setLocaleOverride', { locale });
  } catch (exception) {
    // All pages in the same renderer share locale. All such pages belong to the same
    // context and if locale is overridden for one of them its value is the same as
    // we are trying to set so it's not a problem.
    if (exception.message.includes('Another locale override is already in effect'))
      return;
    throw exception;
  }
}

async function emulateTimezone(session: CRSession, timezoneId: string) {

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Re-resolve the element inside the destination page via a selector rather than passing a handle across documents.
  2. Adopt within the same frame tree only; for popups, query afresh in the popup.
  3. Discard handles after navigations and re-query them.

Example fix

// before
const handle = await pageA.locator('h1').elementHandle();
const text = await pageB.evaluate(h => h.textContent, handle);
// after
const text = await pageB.locator('h1').textContent();
Defensive patterns

Strategy: validation

Validate before calling

// Do not pass ElementHandles across documents; re-query in the destination.
// Instead of carrying a handle from pageA into pageB.evaluate(...):
const text = await pageB.locator('h1').textContent();

Prevention

When it happens

Trigger: Adopting an element handle across documents that do not share a frame tree (different tabs without a parent link, detached documents, OOIF cross-origin boundaries); the backend node was collected; shadow-DOM hosts that cannot be resolved into the isolated world.

Common situations: Passing an ElementHandle from one page into an evaluate on another; moving handles across popup windows; handles captured before a navigation that invalidated them.

Related errors


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