grafana/k6 · error

removing frames recursively: %w

Error message

removing frames recursively: %w

What it means

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.

Source

Thrown at internal/js/modules/k6/browser/common/frame_manager.go:452

func (m *FrameManager) removeChildFramesRecursively(frame *Frame) error {
	for _, child := range frame.ChildFrames() {
		if err := m.removeFramesRecursively(child); err != nil {
			return fmt.Errorf("removing child frames recursively: %w", err)
		}
	}

	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
}

View on GitHub (pinned to 93accf6570)

Solutions

  1. Upgrade k6 first — most instances of this chain were race fixes
  2. Reduce nested-frame churn during the test (block third-party frames) if the app allows
  3. Capture verbose logs and report with a reproducer if it persists on latest
Defensive patterns

Strategy: retry

Type guard

function isFrameCleanupError(e) {
  return e instanceof Error && /removing (child )?frames? recursively/.test(e.message);
}

Try / catch

try {
  await page.goto(url);
} catch (e) {
  if (isFrameCleanupError(e)) { return await page.goto(url); }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: Third-party embed trees (ad tech, video players) several levels deep; timing-sensitive failures during rapid navigation sequences; fixed in several later k6 releases.

Related errors


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