microsoft/playwright · error · Error

Frame has been detached.

Error message

Frame has been detached.

What it means

In `getFrameElement`, when a frame has no parent frame (`parentFrame()` is null), Playwright treats it as detached — the frame is no longer in the frame tree. This guard fires before attempting to locate the frame's element node.

Source

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

    }
  }

  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)
      throw new Error('Frame has been detached.');
    const node = await this._getFrameNode(frame);
    if (!node?.sharedId)
      throw new Error('Frame has been detached.');
    const parentFrameExecutionContext = await parent.mainContext();
    return await toBidiExecutionContext(parentFrameExecutionContext).remoteObjectForNodeId(parentFrameExecutionContext, { sharedId: node.sharedId });
  }

  async _getFrameNode(frame: frames.Frame): Promise<bidi.Script.NodeRemoteValue | undefined> {
    const parent = frame.parentFrame();
    if (!parent)
      return undefined;

    const result = await this._session.send('browsingContext.locateNodes', {
      context: parent._id,
      locator: { type: 'context', value: { context: frame._id } },
    });
    const node = result.nodes[0];
    return node;

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Re-acquire the frame via `page.frame()` / `page.frames()` before calling frameElement()
  2. Check `frame.isDetached()` before use
  3. Don't cache Frame objects across navigations

Example fix

// before
const el = await staleFrame.frameElement();

// after
if (frame.isDetached()) throw new Error('frame gone');
const el = await frame.frameElement();
Defensive patterns

Strategy: validation

Validate before calling

function assertFrameAttached(frame: Frame): void {
  if (frame.isDetached()) throw new Error('Frame is detached; re-acquire via page.frame().');
  if (!frame.parentFrame()) throw new Error('Frame has no parent; cannot resolve frame element.');
}
assertFrameAttached(frame);
const el = await frame.frameElement();

Type guard

function isLiveFrame(frame: Frame): boolean {
  return !frame.isDetached() && !!frame.parentFrame();
}

Prevention

When it happens

Trigger: Calling `frame.frameElement()` (or equivalent) on a frame whose parent reference is gone — the iframe was removed from the DOM or the page navigated away.

Common situations: Holding a stale `Frame` object after its iframe was removed; calling frameElement() after navigation destroyed the frame; detached-frame race during teardown.

Related errors


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