{"record":{"id":"5d27d599c3a37586","repo":"microsoft/playwright","slug":"frame-has-been-detached","errorCode":null,"errorMessage":"Frame has been detached.","messagePattern":"Frame has been detached\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/bidi/bidiPage.ts","lineNumber":717,"sourceCode":"    }\n  }\n\n  async inputActionEpilogue(): Promise<void> {\n  }\n\n  async resetForReuse(progress: Progress): Promise<void> {\n    // See https://github.com/microsoft/playwright/issues/22432.\n    await this.rawMouse.move(progress, 0, 0, 'none', new Set(), new Set(), false);\n  }\n\n  async pdf(options: channels.PagePdfParams): Promise<Buffer> {\n    return this._pdf.generate(options);\n  }\n\n  async getFrameElement(frame: frames.Frame): Promise<dom.ElementHandle> {\n    const parent = frame.parentFrame();\n    if (!parent)\n      throw new Error('Frame has been detached.');\n    const node = await this._getFrameNode(frame);\n    if (!node?.sharedId)\n      throw new Error('Frame has been detached.');\n    const parentFrameExecutionContext = await parent.mainContext();\n    return await toBidiExecutionContext(parentFrameExecutionContext).remoteObjectForNodeId(parentFrameExecutionContext, { sharedId: node.sharedId });\n  }\n\n  async _getFrameNode(frame: frames.Frame): Promise<bidi.Script.NodeRemoteValue | undefined> {\n    const parent = frame.parentFrame();\n    if (!parent)\n      return undefined;\n\n    const result = await this._session.send('browsingContext.locateNodes', {\n      context: parent._id,\n      locator: { type: 'context', value: { context: frame._id } },\n    });\n    const node = result.nodes[0];\n    return node;","sourceCodeStart":699,"sourceCodeEnd":735,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/playwright-core/src/server/bidi/bidiPage.ts#L699-L735","documentation":"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.","triggerScenarios":"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.","commonSituations":"Holding a stale `Frame` object after its iframe was removed; calling frameElement() after navigation destroyed the frame; detached-frame race during teardown.","solutions":["Re-acquire the frame via `page.frame()` / `page.frames()` before calling frameElement()","Check `frame.isDetached()` before use","Don't cache Frame objects across navigations"],"exampleFix":"// before\nconst el = await staleFrame.frameElement();\n\n// after\nif (frame.isDetached()) throw new Error('frame gone');\nconst el = await frame.frameElement();","handlingStrategy":"validation","validationCode":"function assertFrameAttached(frame: Frame): void {\n  if (frame.isDetached()) throw new Error('Frame is detached; re-acquire via page.frame().');\n  if (!frame.parentFrame()) throw new Error('Frame has no parent; cannot resolve frame element.');\n}\nassertFrameAttached(frame);\nconst el = await frame.frameElement();","typeGuard":"function isLiveFrame(frame: Frame): boolean {\n  return !frame.isDetached() && !!frame.parentFrame();\n}","tryCatchPattern":null,"preventionTips":["Check `frame.isDetached()` before operating on a Frame","Re-acquire frames via `page.frame()` / `page.frames()` rather than caching","Avoid holding Frame references across navigations"],"tags":["bidi","frames","detached","stale"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}