{"record":{"id":"fb7d226c6391075c","repo":"grafana/k6","slug":"getting-new-document-handle-w","errorCode":null,"errorMessage":"getting new document handle: %w","messagePattern":"getting new document handle: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/frame.go","lineNumber":257,"sourceCode":"\treturn nil\n}\n\nfunc (f *Frame) defaultTimeout() time.Duration {\n\treturn f.manager.timeoutSettings.timeout()\n}\n\nfunc (f *Frame) document() (*ElementHandle, error) {\n\tf.log.Debugf(\"Frame:document\", \"fid:%s furl:%q\", f.ID(), f.URL())\n\n\tif cdh, ok := f.cachedDocumentHandle(); ok {\n\t\treturn cdh, nil\n\t}\n\n\tf.waitForExecutionContext(mainWorld)\n\n\tdh, err := f.newDocumentHandle()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"getting new document handle: %w\", err)\n\t}\n\n\t// each execution context switch modifies documentHandle.\n\t// see: nullContext().\n\tf.executionContextMu.Lock()\n\tdefer f.executionContextMu.Unlock()\n\tf.documentHandle = dh\n\n\treturn dh, nil\n}\n\nfunc (f *Frame) cachedDocumentHandle() (*ElementHandle, bool) {\n\t// each execution context switch modifies documentHandle.\n\t// see: nullContext().\n\tf.executionContextMu.RLock()\n\tdefer f.executionContextMu.RUnlock()\n\n\treturn f.documentHandle, f.documentHandle != nil","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/frame.go#L239-L275","documentation":"Frame.document needs the frame's document element handle; when no cached handle exists it calls newDocumentHandle (which evaluates 'document' in the main world) and wraps any failure with this message. The evaluate fails when the main-world execution context is gone — the frame navigated (contexts are destroyed and recreated on navigation), the frame was detached, or the target is closing.","triggerScenarios":"Calling frame/page actions that need the document (waitForSelector, element queries) during a navigation; acting on a frame just detached from the DOM; actions racing page.close().","commonSituations":"Doing queries immediately after a click-induced navigation without waiting; scripts that keep using a frame after its iframe is removed; shutdown races at iteration end.","solutions":["Await the navigation (waitForNavigation / waitForLoadState) before querying","Retry the query once after the load state settles","Stop using frames after removing their iframe element","Give the action a timeout budget so k6's internal retries can work"],"exampleFix":"// before\nawait page.click('#go');\nawait page.waitForSelector('#result'); // races the navigation\n\n// after\nawait Promise.all([page.waitForNavigation(), page.click('#go')]);\nawait page.waitForSelector('#result');","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"async function queryStable(page, sel, tries = 3) {\n  for (let i = 0; i < tries; i++) {\n    try { return await page.waitForSelector(sel); }\n    catch (e) {\n      if (!/new document handle|Execution context|navigated/.test(e.message) || i === tries - 1) throw e;\n      await page.waitForLoadState();\n    }\n  }\n}","preventionTips":["Synchronize on load state after navigation-triggering actions","Do not hold frame references across navigations","Scope per-frame work tightly; re-resolve frames from the page after transitions"],"tags":["browser","frame","navigation","race-condition"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}