{"record":{"id":"fd8166c9e2af4771","repo":"grafana/k6","slug":"waiting-for-function-polling-w","errorCode":null,"errorMessage":"waiting for function, polling: %w","messagePattern":"waiting for function, polling: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/frame.go","lineNumber":2018,"sourceCode":"\t// First evaluate the predicate function itself to get its handle.\n\topts := evalOptions{forceCallable: false, returnByValue: false}\n\thandle, err := execCtx.eval(apiCtx, opts, js)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"waiting for function, getting handle: %w\", err)\n\t}\n\n\t// Then evaluate the injected function call, passing it the predicate\n\t// function handle and the rest of the arguments.\n\topts = evalOptions{forceCallable: true, returnByValue: false}\n\tresult, err := execCtx.eval(\n\t\tapiCtx, opts, pageFn, append([]any{\n\t\t\tinjected,\n\t\t\thandle,\n\t\t\tpolling,\n\t\t\ttimeout.Milliseconds(), // The JS value is in ms integers\n\t\t}, args...)...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"waiting for function, polling: %w\", err)\n\t}\n\t// prevent passing a non-nil interface to the upper layers.\n\tif result == nil {\n\t\treturn nil, nil //nolint:nilnil\n\t}\n\n\treturn result, nil\n}\n\n// WaitForLoadState waits for the given load state to be reached.\n// This will unblock if that lifecycle event has already been received.\nfunc (f *Frame) WaitForLoadState(state string, popts *FrameWaitForLoadStateOptions) error {\n\tf.log.Debugf(\"Frame:WaitForLoadState\", \"fid:%s furl:%q state:%s\", f.ID(), f.URL(), state)\n\tdefer f.log.Debugf(\"Frame:WaitForLoadState:return\", \"fid:%s furl:%q state:%s\", f.ID(), f.URL(), state)\n\n\ttimeoutCtx, timeoutCancel := context.WithTimeout(f.ctx, popts.Timeout)\n\tdefer timeoutCancel()\n","sourceCodeStart":2000,"sourceCodeEnd":2036,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/common/frame.go#L2000-L2036","documentation":"Thrown by Frame.WaitForFunction when the injected polling script (the helper that repeatedly calls your predicate inside the page) cannot be evaluated or the predicate's evaluation chain fails. The %w wraps the underlying CDP Runtime.evaluate error, so the real cause (JS exception in the predicate, destroyed execution context, or timeout) is in the wrapped message. This is k6's browser module translating a failed in-page polling loop into a Go error.","triggerScenarios":"Calling page.waitForFunction(fn, arg, {polling: 'raf'|interval, timeout}) where fn throws a JavaScript exception on every poll; the frame navigates while polling is in flight (execution context destroyed); the timeout expires and the injected script rejects; passing a polling value the injected helper cannot use.","commonSituations":"A predicate that references a DOM node that disappears during the wait; calling waitForFunction immediately after page.goto while the document is still swapping contexts; very short timeouts on pages where the condition only becomes true late; k6 versions where context-destruction races during navigation surfaced here.","solutions":["Wrap the predicate body in try/catch and return false on internal errors so a transient JS error does not abort polling","Make sure a navigation is not in progress: await page.goto(...) or page.waitForLoadState() before waitForFunction","Increase the timeout option (default 30s) to cover slow pages","Read the wrapped (%w) error text: 'context destroyed' points to navigation races, a JS stack points to your predicate"],"exampleFix":"// before\npage.waitForFunction(() => window.results.length > 0);\n\n// after\npage.waitForFunction(\n  () => { try { return window.results.length > 0; } catch { return false; } },\n  { timeout: 60_000, polling: 500 }\n);","handlingStrategy":"try-catch","validationCode":"if (typeof page.waitForFunction !== 'function') throw new Error('page is closed or invalid');","typeGuard":"function isWaitForFunctionTimeout(e) {\n  return e instanceof Error && /waiting for function, polling/.test(e.message);\n}","tryCatchPattern":"try {\n  await page.waitForFunction(() => window.ready === true, { timeout: 60_000, polling: 500 });\n} catch (e) {\n  if (isWaitForFunctionTimeout(e)) { /* re-check condition once, then fail with context */ }\n  throw e;\n}","preventionTips":["Wrap predicate bodies in try/catch returning false","Ensure no navigation is in flight before waiting","Set an explicit timeout larger than the worst observed condition time"],"tags":["browser","wait-for-function","javascript","cdp"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}