grafana/k6 · error
no frame found for node: %w
Error message
no frame found for node: %w
What it means
OwnerFrame() got a successful DOM.describeNode response but the returned node is nil or has an empty FrameID (element_handle.go:1159). Note the message wraps the already-nil err, so it renders as 'no frame found for node: %!w(<nil>)' — a formatting quirk that tells you this is a detached/frame-less node, not a transport failure.
Source
Thrown at internal/js/modules/k6/browser/common/element_handle.go:1159
}
defer func() {
if err := documentHandle.Dispose(); err != nil {
err = fmt.Errorf("disposing document element: %w", err)
rerr = errors.Join(err, rerr)
}
}()
if documentHandle.remoteObject.ObjectID == "" {
return nil, err
}
var node *cdp.Node
action := dom.DescribeNode().WithObjectID(documentHandle.remoteObject.ObjectID)
if node, err = action.Do(cdp.WithExecutor(h.ctx, h.session)); err != nil {
return nil, fmt.Errorf("getting node in frame: %w", err)
}
if node == nil || node.FrameID == "" {
return nil, fmt.Errorf("no frame found for node: %w", err)
}
frame, ok := h.frame.manager.getFrameByID(node.FrameID)
if !ok {
return nil, fmt.Errorf("no frame found for id %s", node.FrameID)
}
return frame, nil
}
// Press scrolls element into view and presses the given keys.
func (h *ElementHandle) Press(key string, opts *ElementHandlePressOptions) error {
press := func(apiCtx context.Context, handle *ElementHandle) (any, error) {
return nil, handle.press(apiCtx, key, KeyboardOptions{})
}
pressAction := h.newAction(
[]string{}, press, false, withRetry, opts.NoWaitAfter, opts.Timeout,
)View on GitHub (pinned to 93accf6570)
Solutions
- Treat the handle as stale: re-run the query that produced it and retry
- Wait for a stable state (waitForLoadState / waitForSelector visible) before the call
- If you control the code path, check handle validity (isVisible) before frame-dependent operations
- Report the %!w(<nil>) formatting upstream — the wrapped err is always nil here
Example fix
// before
const el = page.$('button');
await page.goto(page.url()); // DOM replaced, el is stale
el.click(); // owner-frame resolution may fail
// after
await page.goto(page.url());
const el = await page.waitForSelector('button', { state: 'visible' });
el.click(); Defensive patterns
Strategy: validation
Validate before calling
// Guard against stale handles before the call
if (!(await el.isVisible())) {
el = await page.waitForSelector(sel, { state: 'visible' });
} Try / catch
try { await el.click(); }
catch (e) { if (/no frame found for node/.test(e.message)) { el = await page.waitForSelector(sel); await el.click(); } else throw e; } Prevention
- Treat this message as 'stale/detached element' — the wrapped error is always nil
- Refresh handles after DOM replacements
- Wait for selector visible state before acting
When it happens
Trigger: The element is detached from the document (stale handle), belongs to a destroyed document, or DescribeNode returns a node that has no frame association (e.g., a document node in a torn-down context).
Common situations: Keeping an ElementHandle across a page navigation or SPA re-render and then calling something that needs the owner frame; iframe content removed between query and use.
Related errors
- expected node but got %s
- errorText
- getting bounding box: %w
- getting remote node %q: %w
- getting node in frame: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/eb3dc4042458a814.
Report an issue: GitHub.