{"record":{"id":"677421acb35b42b5","repo":"grafana/k6","slug":"frame-has-been-detached","errorCode":null,"errorMessage":"frame has been detached","messagePattern":"frame has been detached","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/page.go","lineNumber":669,"sourceCode":"\tparent := f.parentFrame\n\tif parent == nil {\n\t\treturn nil, errors.New(\"frame has been detached 1\")\n\t}\n\n\trootFrame := f\n\tfor ; rootFrame.parentFrame != nil; rootFrame = rootFrame.parentFrame {\n\t}\n\n\tparentSession, ok := p.getFrameSession(cdp.FrameID(rootFrame.ID()))\n\tif !ok {\n\t\treturn nil, errors.New(\"parent frame has been detached\")\n\t}\n\n\taction := dom.GetFrameOwner(cdp.FrameID(f.ID()))\n\tbackendNodeID, _, err := action.Do(cdp.WithExecutor(p.ctx, parentSession.session))\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"frame with the given id was not found\") {\n\t\t\treturn nil, errors.New(\"frame has been detached\")\n\t\t}\n\t\treturn nil, fmt.Errorf(\"getting frame owner: %w\", err)\n\t}\n\n\tparent = f.parentFrame\n\tif parent == nil {\n\t\treturn nil, errors.New(\"frame has been detached 2\")\n\t}\n\treturn parent.adoptBackendNodeID(mainWorld, backendNodeID)\n}\n\nfunc (p *Page) getOwnerFrame(apiCtx context.Context, h *ElementHandle) (cdp.FrameID, error) {\n\tp.logger.Debugf(\"Page:getOwnerFrame\", \"sid:%v\", p.sessionID())\n\n\t// document.documentElement has frameId of the owner frame\n\tpageFn := `\n\t\tnode => {\n\t\t\tconst doc = node;","sourceCodeStart":651,"sourceCodeEnd":687,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/page.go#L651-L687","documentation":"Page.getFrameElement calls dom.GetFrameOwner to find the <iframe>'s backend node in the parent. When CDP answers 'frame with the given id was not found' (page.go:665-671), the frame was already removed from the DOM, so k6 maps it to 'frame has been detached'.","triggerScenarios":"The <iframe> element was deleted from the DOM (SPA unmounted it) before or during frameElement(); the frame navigated away and was destroyed between discovery and the CDP call.","commonSituations":"Dynamic iframe mounting/unmounting in single-page apps; frame handles captured earlier and used after the page re-rendered; modals that embed iframes being dismissed.","solutions":["Re-discover the frame from page.frames() immediately before calling frameElement()","Wait for the iframe selector (e.g. page.waitForSelector('iframe#widget')) to be attached first","Retry the discover+frameElement sequence when the app frequently re-creates iframes","If the iframe is gone for good, skip the step instead of retrying forever"],"exampleFix":"// before\nconst frame = savedFrame; // captured before a re-render\nconst el = await frame.frameElement(); // CDP: frame not found\n\n// after\nawait page.waitForSelector('iframe#widget');\nconst frame = page.frames().find(f => f.url().includes('widget'));\nconst el = await frame.frameElement();","handlingStrategy":"try-catch","validationCode":"// Confirm the iframe exists in the DOM before using its frame\nawait page.waitForSelector('iframe#widget', { state: 'attached' });\nconst frame = page.frames().find(f => f.url().includes('widget'));\nif (frame) { const el = await frame.frameElement(); }","typeGuard":null,"tryCatchPattern":"try {\n  const el = await frame.frameElement();\n} catch (e) {\n  if (String(e.message).includes('frame has been detached')) {\n    await page.waitForSelector('iframe#widget', { state: 'attached' });\n    frame = page.frames().find(f => f.url().includes('widget'));\n    el = await frame.frameElement();\n  } else { throw e; }\n}","preventionTips":["Wait for the iframe selector to be attached before resolving frame elements","Re-discover frames from page.frames() instead of caching them across re-renders","Skip gracefully when the iframe was intentionally removed by the app"],"tags":["browser","frame","iframe","detached"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}