grafana/k6 · error
frame not found for id %s
Error message
frame not found for id %s
What it means
ContentFrame() resolved a FrameID from the DOM node, but the page's frame manager has no registered frame with that ID. The iframe was detached or navigated away, or its frame-tree entry was never attached (out-of-process iframe attach race).
Source
Thrown at internal/js/modules/k6/browser/common/element_handle.go:867
}
// ContentFrame returns the frame that contains this element.
func (h *ElementHandle) ContentFrame() (*Frame, error) {
var (
node *cdp.Node
err error
)
action := dom.DescribeNode().WithObjectID(h.remoteObject.ObjectID)
if node, err = action.Do(cdp.WithExecutor(h.ctx, h.session)); err != nil {
return nil, fmt.Errorf("getting remote node %q: %w", h.remoteObject.ObjectID, err)
}
if node == nil || node.FrameID == "" {
return nil, fmt.Errorf("element is not an iframe")
}
frame, ok := h.frame.manager.getFrameByID(node.FrameID)
if !ok {
return nil, fmt.Errorf("frame not found for id %s", node.FrameID)
}
return frame, nil
}
// Dblclick scrolls element into view and double clicks on the element.
func (h *ElementHandle) Dblclick(opts *ElementHandleDblclickOptions) error {
dblclick := func(_ context.Context, handle *ElementHandle, p *Position) (any, error) {
return nil, handle.dblclick(p, opts.ToMouseClickOptions())
}
dblclickAction := h.newPointerAction(dblclick, &opts.ElementHandleBasePointerOptions)
if _, err := call(h.ctx, dblclickAction, opts.Timeout); err != nil {
return fmt.Errorf("double clicking on element: %w", err)
}
applySlowMo(h.ctx)
return nilView on GitHub (pinned to 93accf6570)
Solutions
- Retry after a short wait — frame attachment is often just racing
- Wait for a known selector inside the iframe instead of grabbing the frame directly
- Prefer page.frameLocator(), which re-resolves the frame chain on each action
- Increase the timeout so iframe attachment completes
Example fix
// before
const frame = await (await page.$('#lateframe')).contentFrame(); // frame not found for id ...
// after
await page.waitForSelector('#lateframe >> .ready');
const frame = await (await page.$('#lateframe')).contentFrame(); Defensive patterns
Strategy: retry
Try / catch
async function getContentFrame(page, sel, tries = 3) {
for (let i = 0; i < tries; i++) {
try { return await (await page.$(sel)).contentFrame(); }
catch (e) {
if (!String(e).includes('frame not found')) throw e;
await sleep(500); // frame tree still attaching
}
}
throw new Error('iframe frame never attached');
} Prevention
- Wait for a selector inside the iframe before using its frame
- Expect attach races right after load and after src swaps
- Prefer frameLocator(), which re-resolves per action
When it happens
Trigger: contentFrame() (directly or via frame-crossing selectors) on an iframe being removed/replaced at that instant, a cross-origin iframe whose OOP frame is not yet attached, or right after the iframe's src changed.
Common situations: Ad iframes swapping in and out; SPA re-renders replacing iframes; querying inside an iframe immediately after load before the frame tree settles.
Related errors
- no frame found for id %s
- translating point to page: %w
- got a nil page frame tree
- attaching iframe target ID %v to session ID %v: %w
- getting frame owner: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/5b5cd6c4a466029e.
Report an issue: GitHub.