can1357/oh-my-pi · error · ToolError
wait(predicate) timed out after ${timeout}ms — predicate nev
Error message
wait(predicate) timed out after ${timeout}ms — predicate never returned truthy What it means
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.
Source
Thrown at packages/coding-agent/src/tools/run-scope.ts:362
await untilAborted(signal, async () => await Bun.sleep(msOrPredicate));
throwIfAborted(signal);
return undefined;
}
if (typeof msOrPredicate !== "function") {
throw new ToolError("wait(...) expects milliseconds (number) or a predicate function to poll");
}
const timeout =
opts?.timeout !== undefined && Number.isFinite(opts.timeout) && opts.timeout > 0
? opts.timeout
: DEFAULT_PREDICATE_TIMEOUT_MS;
const interval = Math.max(opts?.interval ?? 100, 10);
const deadline = Date.now() + timeout;
for (;;) {
const value = await untilAborted(signal, async () => await msOrPredicate());
throwIfAborted(signal);
if (value) return value;
if (Date.now() + interval > deadline) {
throw new ToolError(`wait(predicate) timed out after ${timeout}ms — predicate never returned truthy`);
}
await untilAborted(signal, async () => await Bun.sleep(interval));
}
})();
return trackBrowserRunPromise(promise);
}
/** Binds a long-lived scope facade (page/tab/desktop objects) to one evaluated run's abort signal. */
export function bindRunFacade<T extends object>(
target: T,
signal: AbortSignal,
rejectionOwner?: object,
onFloatingRejection?: FloatingRejectionHandler,
): T {
const cache = new Map<PropertyKey, unknown>();
return new Proxy(target, {
get(current, prop) {
throwIfAborted(signal);View on GitHub (pinned to 9690622007)
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
Example fix
// before
await wait(() => run.status === "complete"); // times out on slow run
// after
await wait(() => run.status === "complete", { timeout: 120000 }); Defensive patterns
Strategy: retry
Validate before calling
// ensure the waited-for condition is achievable before polling
if (!run || run.finished && !expectedCondition(run)) throw new Error("Run ended without reaching the awaited condition"); Try / catch
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; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- {delivery_id} did not reach a terminal state within {timeout
- xAI device-code token polling failed: ${error instanceof Err
- tab.waitForUrl() timed out after ${timeoutMs}ms
- tab.waitForResponse() timed out after ${timeoutMs}ms
- page.waitForFunction() timed out after ${timeoutMs}ms
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/636191ab71dd1cff.
Report an issue: GitHub.