{"record":{"id":"636191ab71dd1cff","repo":"can1357/oh-my-pi","slug":"wait-predicate-timed-out-after-timeout-ms-pre","errorCode":null,"errorMessage":"wait(predicate) timed out after ${timeout}ms — predicate never returned truthy","messagePattern":"wait\\(predicate\\) timed out after (.+?)ms — predicate never returned truthy","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/run-scope.ts","lineNumber":362,"sourceCode":"\t\t\tawait untilAborted(signal, async () => await Bun.sleep(msOrPredicate));\n\t\t\tthrowIfAborted(signal);\n\t\t\treturn undefined;\n\t\t}\n\t\tif (typeof msOrPredicate !== \"function\") {\n\t\t\tthrow new ToolError(\"wait(...) expects milliseconds (number) or a predicate function to poll\");\n\t\t}\n\t\tconst timeout =\n\t\t\topts?.timeout !== undefined && Number.isFinite(opts.timeout) && opts.timeout > 0\n\t\t\t\t? opts.timeout\n\t\t\t\t: DEFAULT_PREDICATE_TIMEOUT_MS;\n\t\tconst interval = Math.max(opts?.interval ?? 100, 10);\n\t\tconst deadline = Date.now() + timeout;\n\t\tfor (;;) {\n\t\t\tconst value = await untilAborted(signal, async () => await msOrPredicate());\n\t\t\tthrowIfAborted(signal);\n\t\t\tif (value) return value;\n\t\t\tif (Date.now() + interval > deadline) {\n\t\t\t\tthrow new ToolError(`wait(predicate) timed out after ${timeout}ms — predicate never returned truthy`);\n\t\t\t}\n\t\t\tawait untilAborted(signal, async () => await Bun.sleep(interval));\n\t\t}\n\t})();\n\treturn trackBrowserRunPromise(promise);\n}\n\n/** Binds a long-lived scope facade (page/tab/desktop objects) to one evaluated run's abort signal. */\nexport function bindRunFacade<T extends object>(\n\ttarget: T,\n\tsignal: AbortSignal,\n\trejectionOwner?: object,\n\tonFloatingRejection?: FloatingRejectionHandler,\n): T {\n\tconst cache = new Map<PropertyKey, unknown>();\n\treturn new Proxy(target, {\n\t\tget(current, prop) {\n\t\t\tthrowIfAborted(signal);","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/run-scope.ts#L344-L380","documentation":"waitForRun polls the predicate on an interval until it returns a truthy value or a deadline (opts.timeout, default DEFAULT_PREDICATE_TIMEOUT_MS) expires. When the predicate stays falsy past the deadline, it throws this ToolError naming the timeout in milliseconds.","triggerScenarios":"wait(predicate) where the predicate never becomes truthy within the timeout — condition never met (e.g. waiting for output text that never appears), timeout too short for a slow run, or predicate checking the wrong field.","commonSituations":"Waiting on a long-running browser/run command whose completion exceeds the default predicate timeout; predicate compares against output that got truncated; status field name typo means it never matches.","solutions":["Increase opts.timeout (e.g. wait(pred, { timeout: 60000 }))","Fix the predicate so it checks the correct field/value","Verify the underlying run actually progresses or completes; if it hangs, kill/restart it","Tune opts.interval if polling overhead matters"],"exampleFix":"// before\nawait wait(() => run.status === \"complete\"); // times out on slow run\n// after\nawait wait(() => run.status === \"complete\", { timeout: 120000 });","handlingStrategy":"retry","validationCode":"// ensure the waited-for condition is achievable before polling\nif (!run || run.finished && !expectedCondition(run)) throw new Error(\"Run ended without reaching the awaited condition\");","typeGuard":null,"tryCatchPattern":"try { await wait(pred); } catch (e) { if (e instanceof ToolError && e.message.includes(\"timed out after\")) { await wait(pred, { timeout: e_TIMEOUT * 2 }); } else throw e; }","preventionTips":["Set an explicit timeout sized to the slowest expected run","Keep the predicate cheap and side-effect free","Verify the watched status/output field actually changes on completion"],"tags":["timeout","polling","async"],"backgroundTag":"poll-timeout","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}