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.Node

View on GitHub (pinned to 93accf6570)

Solutions

  1. Wait for the navigation to settle (page.waitForLoadState) before calling ownerFrame()
  2. Re-query the element handle right before calling ownerFrame()
  3. Verify the frame is still listed in page.frames() before resolving element ownership
  4. 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

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


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