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

  1. Upgrade k6 — detach/teardown races are among the most-fixed areas in the browser module
  2. Await page and context close at the end of the scenario instead of abandoning them
  3. 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

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


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/91fc5b037c65de3d. Report an issue: GitHub.