grafana/k6 · error
getting document element: nil document
Error message
getting document element: nil document
What it means
ElementHandle.OwnerFrame() (element_handle.go:1120-1136) resolves the frame containing the element by evaluating an injected getDocumentElement script. If the script returns nil, the node is detached or its document exposes no reachable documentElement, so the owner frame cannot be determined and the call fails with 'getting document element: nil document'.
Source
Thrown at internal/js/modules/k6/browser/common/element_handle.go:1135
}
// OwnerFrame returns the frame containing this element.
func (h *ElementHandle) OwnerFrame() (_ *Frame, rerr error) {
fn := `
(node, injected) => {
return injected.getDocumentElement(node);
}
`
opts := evalOptions{
forceCallable: true,
returnByValue: false,
}
res, err := h.evalWithScript(h.ctx, opts, fn)
if err != nil {
return nil, fmt.Errorf("getting document element: %w", err)
}
if res == nil {
return nil, errors.New("getting document element: nil document")
}
documentHandle, ok := res.(*ElementHandle)
if !ok {
return nil, fmt.Errorf("unexpected result type while getting document element: %T", res)
}
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.NodeView on GitHub (pinned to 93accf6570)
Solutions
- Wait for the navigation to settle (page.waitForLoadState) before calling ownerFrame()
- Re-query the element handle right before calling ownerFrame()
- Verify the frame is still listed in page.frames() before resolving element ownership
- Retry the call once after a short delay — the nil result is usually a transient race
Example fix
// before
const el = await page.$('#in-iframe');
await page.reload();
const frame = await el.ownerFrame(); // nil document race
// after
await page.reload();
await page.waitForLoadState('load');
const el = await page.$('#in-iframe');
const frame = await el.ownerFrame(); Defensive patterns
Strategy: retry
Validate before calling
// Only resolve owner frames on elements confirmed still attached
const attached = await el.evaluate(node => node.isConnected);
if (attached) { const frame = await el.ownerFrame(); } Try / catch
try {
const frame = await el.ownerFrame();
} catch (e) {
if (String(e.message).includes('nil document')) {
await page.waitForLoadState('load');
el = await page.waitForSelector(sel);
frame = await el.ownerFrame();
} else { throw e; }
} Prevention
- Call ownerFrame() on freshly queried handles only
- Let navigations finish (waitForLoadState) before resolving frame ownership
- Treat ownerFrame as best-effort around SPA transitions and retry once
When it happens
Trigger: Calling elementHandle.ownerFrame() on a handle whose node was removed; running it while a navigation is swapping the document (documentElement transiently missing); on early about:blank documents; after the owning frame was destroyed.
Common situations: SPAs mid-navigation when the old document is torn down; ownerFrame() called on stale handles obtained before a transition; iterating elements collected on a previous page state.
Related errors
- element is not attached to the DOM
- waitFor retry threshold reached
- parent frame has been detached
- frame has been detached 2
- getting node in frame: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/87a60ce4b9f40d80.
Report an issue: GitHub.