can1357/oh-my-pi · error · ToolError
tab.waitForUrl() timed out after ${timeoutMs}ms
Error message
tab.waitForUrl() timed out after ${timeoutMs}ms What it means
CmuxTab.waitForUrl(pattern, opts) polls browser.url.get every 200ms until the current URL matches the given RegExp and throws this ToolError when the deadline (opts.timeout, run-context timeout, or 30s default) expires first. Note: when pattern is a plain string, a native browser.wait is used instead and this error only comes from the RegExp polling path.
Source
Thrown at packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts:645
this.#lastUrl = result.url;
}
return this.#lastUrl;
}
const deadline = Date.now() + timeoutMs;
while (Date.now() <= deadline) {
const result = (await this.#request(
"browser.url.get",
{},
Math.min(timeoutMs, 5_000),
signal,
)) as CmuxUrlGetResult;
if (typeof result.url === "string" && result.url.length > 0) {
this.#lastUrl = result.url;
if (pattern.test(result.url)) return result.url;
}
await untilAborted(signal, () => Bun.sleep(200));
}
throw new ToolError(`tab.waitForUrl() timed out after ${timeoutMs}ms`);
}
async waitForNavigation(opts?: { waitUntil?: WaitUntil; timeout?: number }): Promise<null> {
const timeoutMs = opts?.timeout ?? this.#runContext?.timeoutMs ?? 30_000;
const signal = this.#runContext?.signal;
// Cmux has no native "next navigation" wait — snapshot the current URL via a fresh
// `browser.url.get` (never the possibly-stale `#lastUrl`), then poll for a change
// from it (mirroring headless `page.waitForNavigation` intent) and optionally settle
// on the requested load state. Start it BEFORE the click/submit that navigates; after
// a completed nav it times out like puppeteer does.
const baseline = (await this.#request(
"browser.url.get",
{},
Math.min(timeoutMs, 5_000),
signal,
)) as CmuxUrlGetResult;
const startUrl = typeof baseline.url === "string" && baseline.url.length > 0 ? baseline.url : this.#lastUrl;
if (typeof baseline.url === "string" && baseline.url.length > 0) this.#lastUrl = baseline.url;View on GitHub (pinned to 9690622007)
Solutions
- Increase opts.timeout (e.g. tab.waitForUrl(/dashboard/, { timeout: 60000 })).
- Loosen or correct the RegExp — print the actual URL via tab.evaluate(() => location.href) and adjust the pattern.
- Confirm the triggering action really navigates: if the click silently failed, wait for the element and re-click before waiting.
- For SPA routing, verify the app updates location.href (history.pushState changes it) or poll a page-side condition with tab.waitForFunction() instead.
Example fix
// before: strict regex, times out on hash routing
await tab.waitForUrl(/^https:\/\/app\.example\.com\/dashboard$/);
// after: looser match + larger timeout
await tab.waitForUrl(/dashboard/, { timeout: 60_000 }); Defensive patterns
Strategy: retry
Validate before calling
// validate the pattern matches the current or expected URL shape const current = await tab.evaluate(() => location.href); if (pattern instanceof RegExp && pattern.test(current)) return current; // already there
Try / catch
try {
await tab.waitForUrl(/dashboard/, { timeout: 30_000 });
} catch (err) {
if (err instanceof ToolError && err.message.includes("waitForUrl() timed out")) {
const actual = await tab.evaluate(() => location.href);
throw new Error(`waitForUrl failed; still at ${actual}`);
}
throw err;
} Prevention
- Test your RegExp against the real URL (log location.href) before relying on it
- Pass an explicit timeout sized for the slowest expected navigation
- For SPAs, confirm the route updates location or poll a DOM condition instead
- Ensure the triggering action (click/submit) actually succeeded before waiting
When it happens
Trigger: Calling tab.waitForUrl(/regex/) after an action that never actually navigated (failed click, blocked popup, SPA route change that doesn't update location), a pattern that never matches the resulting URL, or navigation slower than the timeout.
Common situations: Waiting for a post-login redirect that was blocked by captcha/2FA; a RegExp written against an expected URL that the app changed (query-param or hash routing differences); SPA client-side routing that rewrites history without a full navigation the daemon observes; timeout too short for slow networks.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- tab.waitForNavigation() timed out after ${timeoutMs}ms
- tab.waitForResponse() timed out after ${timeoutMs}ms
- page.waitForFunction() timed out after ${timeoutMs}ms
- xAI device-code token polling failed: ${error instanceof Err
- Timed out waiting for CDP endpoint ${cdpUrl}${lastStatus !==
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/99eb9fb78bbe5625.
Report an issue: GitHub.