grafana/k6 · error

parent frame has been detached

Error message

parent frame has been detached

What it means

In Page.getFrameElement, after walking to the root frame, k6 needs the CDP session of that root frame's target. If p.getFrameSession(rootFrame.ID()) finds none (page.go:658-664), the target/session is already gone and the frame tree is unreachable — reported as 'parent frame has been detached'.

Source

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

	if f == nil {
		p.logger.Debugf("Page:getFrameElement", "sid:%v frame:nil", p.sessionID())
	} else {
		p.logger.Debugf("Page:getFrameElement", "sid:%v fid:%s furl:%s",
			p.sessionID(), f.ID(), f.URL())
	}

	parent := f.parentFrame
	if parent == nil {
		return nil, errors.New("frame has been detached 1")
	}

	rootFrame := f
	for ; rootFrame.parentFrame != nil; rootFrame = rootFrame.parentFrame {
	}

	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)
}

View on GitHub (pinned to 93accf6570)

Solutions

  1. Ensure the owning page and browser context are still open before calling frameElement()
  2. Re-acquire the page and frames from a live context instead of reusing references from a previous iteration
  3. Don't call context.close() while frame operations are pending
  4. Retry frame discovery if the page is expected to recover

Example fix

// before
const page = pages[i];
await context.close();
const el = await page.frames()[1].frameElement(); // session gone

// after
const el = await page.frames()[1].frameElement();
await context.close(); // close only after frame work finishes
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the page is live before frame queries
const frames = page.frames(); // throws/empty if page or context is gone
if (frames.length > 1) { const el = await frames[1].frameElement(); }

Try / catch

try {
  const el = await frame.frameElement();
} catch (e) {
  if (String(e.message).includes('parent frame has been detached')) {
    // page/target closed or crashing: re-acquire page + frame from a live context
    page = await context.newPage(); await page.goto(url);
  } else { throw e; }
}

Prevention

When it happens

Trigger: The page or out-of-process iframe target was closed or crashed before frameElement() ran; the browser context is being torn down (context.close()) while frame queries are in flight; cross-origin iframe processes destroyed.

Common situations: Racing page.close()/context.close() with pending frame operations; renderer crashes under load; scripts that keep frame references across iterations after the page was closed.

Related errors


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