grafana/k6 · error
removing frames recursively: detaching frame: %w
Error message
removing frames recursively: detaching frame: %w
What it means
The leaf error of the frame-removal chain: FrameManager.removeFramesRecursively reports that Frame.detach() itself failed. detach() marks the frame detached, unlinks it from the parent, and disposes the document handle over CDP; if that disposal (or the session teardown it triggers) errors — often because the browser already destroyed the context — the error surfaces here and is wrapped by the recursive callers.
Source
Thrown at internal/js/modules/k6/browser/common/frame_manager.go:457
}
}
return nil
}
func (m *FrameManager) removeFramesRecursively(frame *Frame) error {
for _, child := range frame.ChildFrames() {
m.logger.Debugf("FrameManager:removeFramesRecursively",
"fmid:%d cfid:%v pfid:%v cfname:%s cfurl:%s",
m.ID(), child.ID(), frame.ID(), child.Name(), child.URL())
if err := m.removeFramesRecursively(child); err != nil {
return fmt.Errorf("removing frames recursively: %w", err)
}
}
if err := frame.detach(); err != nil {
return fmt.Errorf("removing frames recursively: detaching frame: %w", err)
}
m.framesMu.Lock()
m.logger.Debugf("FrameManager:removeFramesRecursively:delParentFrame",
"fmid:%d fid:%v fname:%s furl:%s",
m.ID(), frame.ID(), frame.Name(), frame.URL())
delete(m.frames, cdp.FrameID(frame.ID()))
m.framesMu.Unlock()
return nil
}
func (m *FrameManager) requestFailed(req *Request, canceled bool) {
m.logger.Debugf("FrameManager:requestFailed", "fmid:%d rurl:%s", m.ID(), req.URL())
frame := req.getFrame()
if frame == nil {View on GitHub (pinned to 93accf6570)
Solutions
- Upgrade k6 — detach/teardown races are among the most-fixed areas in the browser module
- Await page and context close at the end of the scenario instead of abandoning them
- If reproducible, run with -v environment=DEBUG and file an issue with the frame IDs from the log
Defensive patterns
Strategy: retry
Type guard
function isFrameDetachError(e) {
return e instanceof Error && /detaching frame/.test(e.message);
} Try / catch
try {
await page.close();
} catch (e) {
if (isFrameDetachError(e)) { /* ignore: target already torn down by browser */ }
else throw e;
} Prevention
- Always await context/page teardown at scenario end
- Treat detach-during-close as benign; treat detach-during-goto as a retry signal
When it happens
Trigger: Detaching a frame whose CDP runtime context was already destroyed (navigation race); disposing a document handle for an executed-context-destroyed world; page/browser closing while frame cleanup runs.
Common situations: Intermittent errors during goto on iframe-heavy pages or during browserContext.close/page.close; older k6 builds; headless-only timing differences in CI.
Related errors
- can't fetch the page for unknown reason
- adding k6 object to new browser context: %w
- querying selector %q, wrong type %T
- getting element handle for selector %q: %w
- unexpected select element type %T
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/91fc5b037c65de3d.
Report an issue: GitHub.