can1357/oh-my-pi · error · ToolError
tab.waitForResponse() timed out after ${timeoutMs}ms
Error message
tab.waitForResponse() timed out after ${timeoutMs}ms What it means
CmuxTab.waitForResponse(pattern, opts) installs a response observer, then polls recorded network responses every 100ms until one matches the string/RegExp/predicate pattern. This ToolError is thrown when the timeout (opts.timeout, run-context timeout, or 30s default) expires with no matching response.
Source
Thrown at packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts:748
): Promise<CmuxResponse> {
const timeoutMs = opts?.timeout ?? this.#runContext?.timeoutMs ?? 30_000;
const signal = this.#runContext?.signal;
await this.#installResponseObserver();
const startId = await this.#responseCursor();
const deadline = Date.now() + timeoutMs;
while (Date.now() <= deadline) {
const records = await this.#responseRecordsAfter(startId);
for (const record of records) {
const response = new CmuxResponse(record);
if (typeof pattern === "function") {
if (await pattern(response)) return response;
} else if (pattern instanceof RegExp ? pattern.test(record.url) : record.url.includes(pattern)) {
return response;
}
}
await untilAborted(signal, () => Bun.sleep(100));
}
throw new ToolError(`tab.waitForResponse() timed out after ${timeoutMs}ms`);
}
async id(id: number): Promise<CmuxElementHandle> {
const ref = this.#elementRefs.get(id)?.ref ?? `@e${id}`;
await this.#waitForSelector(ref, this.#runContext?.timeoutMs ?? 30_000);
return new CmuxElementHandle(this, ref);
}
ensureRuntime(session: SessionSnapshot): JsRuntime {
if (!this.#runtime) {
this.#runtime = new JsRuntime({
initialCwd: session.cwd,
sessionId: `cmux-tab-${this.#surfaceId}`,
});
}
return this.#runtime;
}
View on GitHub (pinned to 9690622007)
Solutions
- Increase opts.timeout (e.g. tab.waitForResponse(/api\/save/, { timeout: 60000 })).
- Loosen the pattern: match a stable substring of the path instead of the full URL with anchors; use a predicate to also match method/status.
- Install the observer and start waitForResponse BEFORE the action that triggers the request (await the promise around the click) so early responses aren't missed.
- Confirm the request is actually made — check via tab.evaluate / network tab; if the click failed, fix the trigger first.
Example fix
// before: anchored regex misses query params, wait started after click
await tab.click("#save");
await tab.waitForResponse(/^https:\/\/app\.example\.com\/api\/save$/);
// after: arm first, substring match, longer timeout
const res = tab.waitForResponse("/api/save", { timeout: 60_000 });
await tab.click("#save");
await res; Defensive patterns
Strategy: retry
Validate before calling
// ensure a matching request will be possible: arm before the action
const pending = tab.waitForResponse("/api/save", { timeout: 30_000 });
// ...triggering click happens here... Try / catch
const pending = tab.waitForResponse("/api/save", { timeout: 60_000 });
await tab.click("#save");
try {
const res = await pending;
} catch (err) {
if (err instanceof ToolError && err.message.includes("waitForResponse() timed out")) {
throw new Error("save request never fired — check that #save click triggered it");
}
throw err;
} Prevention
- Start waitForResponse before the action that triggers the request
- Match on stable URL substrings, not anchored regexes over full URLs with query strings
- Use predicate callbacks to match method/status, and keep them side-effect free
- Size timeouts to the slowest expected endpoint (especially third-party APIs)
When it happens
Trigger: Waiting for an XHR/fetch that the triggering action never sent (click didn't fire the request); the pattern doesn't match the real URL (query-string differences, relative vs absolute, or pattern tested only against record.url — not method/body); the response arrives after the deadline; the observer was installed after the request already completed.
Common situations: Predicate callbacks that throw inside the poll loop or match on response bodies not captured by the record; API requests issued by a service worker or different frame not visible to the observer; regexes anchored (^...$) against URLs carrying cache-busting query params; slow third-party endpoints exceeding the default 30s.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- xAI device-code token polling failed: ${error instanceof Err
- Timed out waiting for CDP endpoint ${cdpUrl}${lastStatus !==
- tab.waitForUrl() timed out after ${timeoutMs}ms
- page.waitForFunction() timed out after ${timeoutMs}ms
- Google browser search timed out.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/bfa7906f4ac80594.
Report an issue: GitHub.