{"record":{"id":"aa19d73807a0aa3d","repo":"grafana/k6","slug":"removing-frames-recursively-w","errorCode":null,"errorMessage":"removing frames recursively: %w","messagePattern":"removing frames recursively: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/frame_manager.go","lineNumber":452,"sourceCode":"\nfunc (m *FrameManager) removeChildFramesRecursively(frame *Frame) error {\n\tfor _, child := range frame.ChildFrames() {\n\t\tif err := m.removeFramesRecursively(child); err != nil {\n\t\t\treturn fmt.Errorf(\"removing child frames recursively: %w\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (m *FrameManager) removeFramesRecursively(frame *Frame) error {\n\tfor _, child := range frame.ChildFrames() {\n\t\tm.logger.Debugf(\"FrameManager:removeFramesRecursively\",\n\t\t\t\"fmid:%d cfid:%v pfid:%v cfname:%s cfurl:%s\",\n\t\t\tm.ID(), child.ID(), frame.ID(), child.Name(), child.URL())\n\n\t\tif err := m.removeFramesRecursively(child); err != nil {\n\t\t\treturn fmt.Errorf(\"removing frames recursively: %w\", err)\n\t\t}\n\t}\n\n\tif err := frame.detach(); err != nil {\n\t\treturn fmt.Errorf(\"removing frames recursively: detaching frame: %w\", err)\n\t}\n\n\tm.framesMu.Lock()\n\tm.logger.Debugf(\"FrameManager:removeFramesRecursively:delParentFrame\",\n\t\t\"fmid:%d fid:%v fname:%s furl:%s\",\n\t\tm.ID(), frame.ID(), frame.Name(), frame.URL())\n\n\tdelete(m.frames, cdp.FrameID(frame.ID()))\n\tm.framesMu.Unlock()\n\n\treturn nil\n}\n","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/frame_manager.go#L434-L470","documentation":"Thrown by FrameManager.removeFramesRecursively when removing any deeper descendant frame fails (the recursive case of the detach chain; the direct detach failure is error 837). It propagates up through removeChildFramesRecursively (835) and the frameNavigated cleanup (834). Root cause is always the underlying detach error — typically documentHandle.Dispose() or frame-session teardown failing during tree removal.","triggerScenarios":"Deeply nested iframes (iframe within iframe) where an inner frame's detach fails while the whole subtree is being removed on navigation or page close; CDP session already invalidated by the browser when k6 tries to dispose handles.","commonSituations":"Third-party embed trees (ad tech, video players) several levels deep; timing-sensitive failures during rapid navigation sequences; fixed in several later k6 releases.","solutions":["Upgrade k6 first — most instances of this chain were race fixes","Reduce nested-frame churn during the test (block third-party frames) if the app allows","Capture verbose logs and report with a reproducer if it persists on latest"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"function isFrameCleanupError(e) {\n  return e instanceof Error && /removing (child )?frames? recursively/.test(e.message);\n}","tryCatchPattern":"try {\n  await page.goto(url);\n} catch (e) {\n  if (isFrameCleanupError(e)) { return await page.goto(url); }\n  throw e;\n}","preventionTips":["Reduce nested-iframe churn during navigations","Report persistent reproductions with DEBUG logs"],"tags":["browser","frame-manager","iframe","internal","race"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}