{"record":{"id":"a25f5fcb9ad3c899","repo":"grafana/k6","slug":"parent-frame-has-been-detached","errorCode":null,"errorMessage":"parent frame has been detached","messagePattern":"parent frame has been detached","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/page.go","lineNumber":662,"sourceCode":"\tif f == nil {\n\t\tp.logger.Debugf(\"Page:getFrameElement\", \"sid:%v frame:nil\", p.sessionID())\n\t} else {\n\t\tp.logger.Debugf(\"Page:getFrameElement\", \"sid:%v fid:%s furl:%s\",\n\t\t\tp.sessionID(), f.ID(), f.URL())\n\t}\n\n\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","sourceCodeStart":644,"sourceCodeEnd":680,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/page.go#L644-L680","documentation":"In Page.getFrameElement, after walking to the root frame, k6 needs the CDP session of that root frame's target. If p.getFrameSession(rootFrame.ID()) finds none (page.go:658-664), the target/session is already gone and the frame tree is unreachable — reported as 'parent frame has been detached'.","triggerScenarios":"The page or out-of-process iframe target was closed or crashed before frameElement() ran; the browser context is being torn down (context.close()) while frame queries are in flight; cross-origin iframe processes destroyed.","commonSituations":"Racing page.close()/context.close() with pending frame operations; renderer crashes under load; scripts that keep frame references across iterations after the page was closed.","solutions":["Ensure the owning page and browser context are still open before calling frameElement()","Re-acquire the page and frames from a live context instead of reusing references from a previous iteration","Don't call context.close() while frame operations are pending","Retry frame discovery if the page is expected to recover"],"exampleFix":"// before\nconst page = pages[i];\nawait context.close();\nconst el = await page.frames()[1].frameElement(); // session gone\n\n// after\nconst el = await page.frames()[1].frameElement();\nawait context.close(); // close only after frame work finishes","handlingStrategy":"try-catch","validationCode":"// Ensure the page is live before frame queries\nconst frames = page.frames(); // throws/empty if page or context is gone\nif (frames.length > 1) { const el = await frames[1].frameElement(); }","typeGuard":null,"tryCatchPattern":"try {\n  const el = await frame.frameElement();\n} catch (e) {\n  if (String(e.message).includes('parent frame has been detached')) {\n    // page/target closed or crashing: re-acquire page + frame from a live context\n    page = await context.newPage(); await page.goto(url);\n  } else { throw e; }\n}","preventionTips":["Keep the browser context open until all frame work finishes","Don't close pages/contexts while frame operations are in flight","Re-acquire pages and frames per iteration instead of reusing closed references"],"tags":["browser","frame","cdp","race-condition"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}