{"record":{"id":"87a60ce4b9f40d80","repo":"grafana/k6","slug":"getting-document-element-nil-document","errorCode":null,"errorMessage":"getting document element: nil document","messagePattern":"getting document element: nil document","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/element_handle.go","lineNumber":1135,"sourceCode":"}\n\n// OwnerFrame returns the frame containing this element.\nfunc (h *ElementHandle) OwnerFrame() (_ *Frame, rerr error) {\n\tfn := `\n\t\t(node, injected) => {\n\t\t\treturn injected.getDocumentElement(node);\n\t\t}\n\t`\n\topts := evalOptions{\n\t\tforceCallable: true,\n\t\treturnByValue: false,\n\t}\n\tres, err := h.evalWithScript(h.ctx, opts, fn)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"getting document element: %w\", err)\n\t}\n\tif res == nil {\n\t\treturn nil, errors.New(\"getting document element: nil document\")\n\t}\n\n\tdocumentHandle, ok := res.(*ElementHandle)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unexpected result type while getting document element: %T\", res)\n\t}\n\tdefer func() {\n\t\tif err := documentHandle.Dispose(); err != nil {\n\t\t\terr = fmt.Errorf(\"disposing document element: %w\", err)\n\t\t\trerr = errors.Join(err, rerr)\n\t\t}\n\t}()\n\n\tif documentHandle.remoteObject.ObjectID == \"\" {\n\t\treturn nil, err\n\t}\n\n\tvar node *cdp.Node","sourceCodeStart":1117,"sourceCodeEnd":1153,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/element_handle.go#L1117-L1153","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst el = await page.$('#in-iframe');\nawait page.reload();\nconst frame = await el.ownerFrame(); // nil document race\n\n// after\nawait page.reload();\nawait page.waitForLoadState('load');\nconst el = await page.$('#in-iframe');\nconst frame = await el.ownerFrame();","handlingStrategy":"retry","validationCode":"// Only resolve owner frames on elements confirmed still attached\nconst attached = await el.evaluate(node => node.isConnected);\nif (attached) { const frame = await el.ownerFrame(); }","typeGuard":null,"tryCatchPattern":"try {\n  const frame = await el.ownerFrame();\n} catch (e) {\n  if (String(e.message).includes('nil document')) {\n    await page.waitForLoadState('load');\n    el = await page.waitForSelector(sel);\n    frame = await el.ownerFrame();\n  } else { throw e; }\n}","preventionTips":["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"],"tags":["browser","element-handle","frame","race-condition"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}