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

`adoptElementHandle` re-throws a generic message when BiDi's `remoteObjectForNodeId` fails to re-parent a node into a different frame's execution context. Cross-document node adoption is limited under BiDi; the catch hides the underlying BiDi error behind `kUnableToAdoptErrorMessage`.

Source

Thrown at packages/playwright-core/src/server/bidi/bidiPage.ts:698

  }

  async setInputFilePaths(progress: Progress, handle: dom.ElementHandle<HTMLInputElement>, paths: string[]): Promise<void> {
    const fromContext = toBidiExecutionContext(handle._context);
    await progress.race(this._session.send('input.setFiles', {
      context: this._session.sessionId,
      element: await progress.race(fromContext.nodeIdForElementHandle(handle)),
      files: paths,
    }));
  }

  async adoptElementHandle<T extends Node>(handle: dom.ElementHandle<T>, to: dom.FrameExecutionContext): Promise<dom.ElementHandle<T>> {
    const fromContext = toBidiExecutionContext(handle._context);
    const nodeId = await fromContext.nodeIdForElementHandle(handle);
    const executionContext = toBidiExecutionContext(to);
    try {
      return await executionContext.remoteObjectForNodeId(to, nodeId) as dom.ElementHandle<T>;
    } catch {
      throw new Error(dom.kUnableToAdoptErrorMessage);
    }
  }

  async inputActionEpilogue(): Promise<void> {
  }

  async resetForReuse(progress: Progress): Promise<void> {
    // See https://github.com/microsoft/playwright/issues/22432.
    await this.rawMouse.move(progress, 0, 0, 'none', new Set(), new Set(), false);
  }

  async pdf(options: channels.PagePdfParams): Promise<Buffer> {
    return this._pdf.generate(options);
  }

  async getFrameElement(frame: frames.Frame): Promise<dom.ElementHandle> {
    const parent = frame.parentFrame();
    if (!parent)

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Re-query the element in the target frame instead of adopting across frames
  2. Use Locators scoped to the correct frame
  3. Avoid passing ElementHandles across cross-origin frame boundaries

Example fix

// before
const handle = await frame1.locator('button').elementHandle();
await frame2.evaluate(h => h.click(), handle); // cross-frame adoption

// after
await frame2.locator('button').click(); // re-query in target frame
Defensive patterns

Strategy: try-catch

Validate before calling

// Prefer re-querying elements in the target frame rather than adopting handles.
// If you must pass a handle, verify same-origin first:
function sameOrigin(a: URL, b: URL): boolean {
  return a.origin === b.origin;
}

Try / catch

try {
  await targetFrame.evaluate(fn, handleFromOtherFrame);
} catch (e) {
  if (e instanceof Error && /adopt element handle/.test(e.message)) {
    // re-query the element inside the target frame instead
    await targetFrame.locator(selector).click();
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Adopting an element handle from one frame (especially cross-origin) into another via internal `adoptElementHandle` — reached through `frame.evaluate`/locator logic that passes elements across frame boundaries.

Common situations: Cross-origin iframes in Firefox BiDi; passing ElementHandles between frames in evaluate calls; mixins that reuse handles outside their origin frame.

Related errors


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