{"record":{"id":"e7fc90c6b0bc7297","repo":"GopeedLab/gopeed","slug":"unexpected-waitforfunction-payload-t","errorCode":null,"errorMessage":"unexpected waitForFunction payload: %T","messagePattern":"unexpected waitForFunction payload: %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/download/engine/webview/runtime.go","lineNumber":356,"sourceCode":"\t\t\twaitOpts = parseWaitOptions(raw)\n\t\t\tcallArgs = args[1:]\n\t\t}\n\t}\n\n\tvalue, matched, err := p.poll(waitOpts, func() (any, bool, error) {\n\t\tresult, err := p.Execute(`(expression, args) => {\n\t\t\tconst target = (0, eval)(expression);\n\t\t\treturn Promise.resolve(typeof target === \"function\" ? target(...args) : target).then((value) => ({\n\t\t\t\tmatched: !!value,\n\t\t\t\tvalue: value ?? null,\n\t\t\t}));\n\t\t}`, expression, callArgs)\n\t\tif err != nil {\n\t\t\treturn nil, false, err\n\t\t}\n\t\tpayload, ok := result.(map[string]any)\n\t\tif !ok {\n\t\t\treturn nil, false, fmt.Errorf(\"unexpected waitForFunction payload: %T\", result)\n\t\t}\n\t\treturn payload[\"value\"], truthy(payload[\"matched\"]), nil\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !matched {\n\t\treturn nil, nil\n\t}\n\treturn value, nil\n}\n\nfunc (p *PageHandle) GetCookies() ([]Cookie, error) {\n\tpage, err := p.page()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn page.GetCookies()","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/pkg/download/engine/webview/runtime.go#L338-L374","documentation":"PageHandle.WaitForFunction evals a wrapper that must resolve to {matched, value}; the Go side requires the Execute result to deserialize as map[string]any. If the page-side evaluation returns anything else (undefined after a navigation destroyed the context, a serialized primitive, or an unexpected payload), this error names the actual Go type in %T.","triggerScenarios":"Waiting on a function while the page navigates, so the evaluation context is torn down and Execute yields nil; an expression whose eval throws and leaves a non-object result; a webview build whose evaluate bridge returns JSON primitives instead of objects for promise results.","commonSituations":"waitForFunction racing a SPA route change or form submit; waiting on functions that trigger navigation as a side effect; embedded webviews with older CDP/bridge semantics.","solutions":["Make the waited expression side-effect free so it cannot trigger navigation","Catch this error and re-issue WaitForFunction after the navigation settles (it is polled, so a re-call is cheap)","Prefer WaitForSelector for element-based conditions — it tolerates navigation"],"exampleFix":"// before\nawait page.waitForFunction(() => window.location.href.includes('/done'));\n// after (navigation-tolerant)\nlet ok = false;\nfor (let i = 0; i < 10 && !ok; i++) {\n  try { ok = await page.waitForFunction(() => window.ready === true, { timeoutMs: 2000 }); }\n  catch (e) { await page.waitForNavigation?.() ?? null; }\n}","handlingStrategy":"try-catch","validationCode":"// JS: keep the polled expression pure and navigation-free\nawait page.waitForFunction(() => document.readyState === 'complete' && window.__data != null, { timeoutMs: 5000 });","typeGuard":null,"tryCatchPattern":"try {\n  const v = await page.waitForFunction(fn, { timeoutMs: 3000 });\n} catch (e) {\n  if (String(e).includes('unexpected waitForFunction payload')) {\n    // context likely navigated mid-poll: wait for settle, then re-issue once\n    await page.goto(page.url(), { waitUntil: 'domcontentloaded' });\n    return page.waitForFunction(fn, { timeoutMs: 3000 });\n  }\n  throw e;\n}","preventionTips":["Never let the waited function mutate location or submit forms","Prefer waitForSelector for element conditions — it survives navigation","Treat one payload mismatch as transient; two in a row as a real bug"],"tags":["webview","wait","polling","browser-automation"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}