grafana/k6 · error

frame has been detached 2

Error message

frame has been detached 2

What it means

Final stage of getFrameElement (page.go:674-679): DOM.getFrameOwner succeeded and returned a backend node id, but by the time k4 adopts that node, f.parentFrame has become nil — the frame detached in the window between the CDP call and the adoption step. The numbered suffix distinguishes it from the two earlier detach checks in the same function.

Solutions

  1. Retry the whole frameElement() on a freshly fetched frame from page.frames()
  2. Avoid calling frame APIs during known navigation windows — wait for load state first
  3. Reduce concurrent frame operations against the same page during transitions
  4. Upgrade k6 for frame lifecycle race fixes
Defensive patterns

Strategy: retry

Validate before calling

await page.waitForLoadState('load'); // avoid the detach window
const el = await frame.frameElement();

Try / catch

try {
  await frame.frameElement();
} catch (e) {
  if (/detached/.test(e.message)) {
    const fresh = page.frames().find(fr => fr.id === frame.id);
    return fresh ? fresh.frameElement() : null;
  }
  throw e;
}

Prevention

When it happens

Trigger: frameElement() where the frame is detached between the successful GetFrameOwner round-trip and the parent.adoptBackendNodeID call — a pure race, favored by fast navigation or iframe teardown.

Common situations: Fast SPA transitions; CI environments where timing jitter widens the race; loops polling frame properties while the page unloads.

Related errors


AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-08-18). Data as JSON: /api/errors/c4c039caf5a2025c. Report an issue: GitHub.

Appendix: source

Thrown at internal/js/modules/k6/browser/common/page.go:678

	}

	parentSession, ok := p.getFrameSession(cdp.FrameID(rootFrame.ID()))
	if !ok {
		return nil, errors.New("parent frame has been detached")
	}

	action := dom.GetFrameOwner(cdp.FrameID(f.ID()))
	backendNodeID, _, err := action.Do(cdp.WithExecutor(p.ctx, parentSession.session))
	if err != nil {
		if strings.Contains(err.Error(), "frame with the given id was not found") {
			return nil, errors.New("frame has been detached")
		}
		return nil, fmt.Errorf("getting frame owner: %w", err)
	}

	parent = f.parentFrame
	if parent == nil {
		return nil, errors.New("frame has been detached 2")
	}
	return parent.adoptBackendNodeID(mainWorld, backendNodeID)
}

func (p *Page) getOwnerFrame(apiCtx context.Context, h *ElementHandle) (cdp.FrameID, error) {
	p.logger.Debugf("Page:getOwnerFrame", "sid:%v", p.sessionID())

	// document.documentElement has frameId of the owner frame
	pageFn := `
		node => {
			const doc = node;
      		if (doc.documentElement && doc.documentElement.ownerDocument === doc)
        		return doc.documentElement;
      		return node.ownerDocument ? node.ownerDocument.documentElement : null;
		}
	`

	opts := evalOptions{

View on GitHub (pinned to 3fcf5388d7)