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

  1. Increase opts.timeout (e.g. tab.waitForResponse(/api\/save/, { timeout: 60000 })).
  2. 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.
  3. 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.
  4. 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

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

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/bfa7906f4ac80594. Report an issue: GitHub.