microsoft/playwright · error · Error

Frame has been detached.

Error message

Frame has been detached.

What it means

getFrameElement() throws when frame.parentFrame() is null — the frame is the main frame or has no living parent. Without a parent document there is no owner <iframe> element to resolve, so the operation is undefined. Mirrors the wvPage variant but on the standard WKPage backend.

Solutions

  1. Guard with frame.parentFrame() and frame.isDetached() before calling frame.frameElement().
  2. Skip owner-element resolution for the main frame entirely.
  3. Re-acquire frames from page.frames() after navigation.

Example fix

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

// after
if (!frame.parentFrame() || frame.isDetached()) return null;
const el = await frame.frameElement();
Defensive patterns

Strategy: validation

Validate before calling

function canGetOwner(frame) { return !!frame.parentFrame() && !frame.isDetached(); }
if (canGetOwner(frame)) await frame.frameElement();

Type guard

function hasLivingParent(frame: any): boolean { return !!frame?.parentFrame() && !frame.isDetached(); }

Try / catch

try { return await frame.frameElement(); }
catch (e) { if (/Frame has been detached/.test(e.message)) return null; throw e; }

Prevention

When it happens

Trigger: Calling frame.frameElement() (or internal getFrameElement) on page.mainFrame() or on a frame whose parent was detached. Any snapshot/locator path that resolves a frame's owner element.

Common situations: Calling frameElement() on the top frame. Holding a stale child Frame after the parent removed it. Race between detach and an owner-element resolution.

Related errors


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

Appendix: source

Thrown at packages/playwright-core/src/server/webkit/wkPage.ts:1022

    const result = await this._session.sendMayFail('DOM.resolveNode', {
      objectId: handle._objectId,
      executionContextId: (to.delegate as WKExecutionContext)._contextId
    });
    if (!result || result.object.subtype === 'null')
      throw new Error(dom.kUnableToAdoptErrorMessage);
    return createHandle(to, result.object) as dom.ElementHandle<T>;
  }

  async inputActionEpilogue(): Promise<void> {
  }

  async resetForReuse(progress: Progress): Promise<void> {
  }

  async getFrameElement(frame: frames.Frame): Promise<dom.ElementHandle> {
    const parent = frame.parentFrame();
    if (!parent)
      throw new Error('Frame has been detached.');
    const context = await parent.mainContext();
    const result = await this._session.send('DOM.resolveNode', {
      frameId: frame._id,
      executionContextId: (context.delegate as WKExecutionContext)._contextId
    });
    if (!result || result.object.subtype === 'null')
      throw new Error('Frame has been detached.');
    return createHandle(context, result.object) as dom.ElementHandle;
  }

  private _maybeCancelCoopNavigationRequest(provisionalPage: WKProvisionalPage) {
    const navigationRequest = provisionalPage.coopNavigationRequest();
    for (const [requestId, request] of this._requestIdToRequest) {
      if (request.request === navigationRequest) {
        // Make sure the request completes if the provisional navigation is canceled.
        this._onLoadingFailed(provisionalPage._session, {
          requestId: requestId,
          errorText: 'Provisional navigation canceled.',

View on GitHub (pinned to f1d33b5029)