{"record":{"id":"d974438237d5073e","repo":"can1357/oh-my-pi","slug":"tab-waitfornavigation-timed-out-after-timeoutm","errorCode":null,"errorMessage":"tab.waitForNavigation() timed out after ${timeoutMs}ms","messagePattern":"tab\\.waitForNavigation\\(\\) timed out after (.+?)ms","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts","lineNumber":688,"sourceCode":"\t\t\t\tsignal,\n\t\t\t)) as CmuxUrlGetResult;\n\t\t\tif (typeof result.url === \"string\" && result.url.length > 0) {\n\t\t\t\tthis.#lastUrl = result.url;\n\t\t\t\tif (result.url !== startUrl) {\n\t\t\t\t\tif (opts?.waitUntil) {\n\t\t\t\t\t\tawait this.#request(\n\t\t\t\t\t\t\t\"browser.wait\",\n\t\t\t\t\t\t\t{ load_state: mapWaitUntil(opts.waitUntil), timeout_ms: timeoutMs },\n\t\t\t\t\t\t\ttimeoutMs,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait untilAborted(signal, () => Bun.sleep(200));\n\t\t}\n\t\tthrow new ToolError(`tab.waitForNavigation() timed out after ${timeoutMs}ms`);\n\t}\n\n\tasync drag(from: DragTarget, to: DragTarget): Promise<void> {\n\t\tconst start = await this.#dragPoint(from);\n\t\tconst end = await this.#dragPoint(to);\n\t\tawait this.#evalScript(\n\t\t\t`(() => {\n\t\t\t\tconst points = ${JSON.stringify({ start, end })};\n\t\t\t\tconst target = document.elementFromPoint(points.start.x, points.start.y) || document.body;\n\t\t\t\tconst dispatch = (type, point) => target.dispatchEvent(new MouseEvent(type, {\n\t\t\t\t\tbubbles: true,\n\t\t\t\t\tcancelable: true,\n\t\t\t\t\tview: window,\n\t\t\t\t\tclientX: point.x,\n\t\t\t\t\tclientY: point.y,\n\t\t\t\t\tbuttons: type === \"mouseup\" ? 0 : 1,\n\t\t\t\t}));\n\t\t\t\tdispatch(\"mousemove\", points.start);","sourceCodeStart":670,"sourceCodeEnd":706,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts#L670-L706","documentation":"CmuxTab.waitForNavigation() snapshots the current URL and polls browser.url.get every 200ms until it differs from the baseline, throwing this ToolError when the timeout expires without any URL change. Because cmux has no native 'next navigation' wait, the URL must actually change — same-URL navigations or calls made after the navigation completed will time out.","triggerScenarios":"Calling waitForNavigation() after the navigation already finished (baseline taken post-nav, URL never changes again); the triggering click/submit failed or was intercepted; the page navigates to the same URL (e.g. form POST back to itself) so the URL comparison never differs; timeout too short for slow loads.","commonSituations":"Puppeteer-style code that clicks and waits, but the click was swallowed by an overlay or new-tab popup (cmux may not follow target=_blank); SPA route transitions that keep the same URL string; polling during a network stall; forgetting that waitForNavigation must be started BEFORE the action per the documented contract.","solutions":["Start waitForNavigation() BEFORE the click/submit that triggers navigation, then await the click.","Increase opts.timeout for slow pages.","If the URL does not change (same-URL nav or SPA state change), replace with tab.waitForFunction() polling a DOM condition or tab.waitForSelector() on a post-nav element.","Verify the click actually fired: use tab.waitFor(selector) first, scroll the element into view, and check that no overlay intercepts the pointer events."],"exampleFix":"// before: wait after the click already completed\nawait tab.click(\"#submit\");\nawait tab.waitForNavigation();\n// after: arm the wait first, then act\nconst nav = tab.waitForNavigation({ timeout: 60_000 });\nawait tab.click(\"#submit\");\nawait nav;","handlingStrategy":"try-catch","validationCode":"// only meaningful if the URL can actually change\nconst before = await tab.evaluate(() => location.href);\n// if your flow cannot change the URL, prefer waitForFunction instead","typeGuard":null,"tryCatchPattern":"const nav = tab.waitForNavigation({ timeout: 30_000 });\nawait tab.click(\"#submit\");\ntry {\n  await nav;\n} catch (err) {\n  if (err instanceof ToolError && err.message.includes(\"waitForNavigation() timed out\")) {\n    await tab.waitForSelector(\"[data-loaded='true']\", { timeout: 30_000 }); // same-URL fallback\n  } else throw err;\n}","preventionTips":["Always arm waitForNavigation BEFORE the triggering action","Use waitForSelector/waitForFunction for same-URL or SPA-only transitions","Set explicit timeouts appropriate to network conditions","Verify the click target is visible and not covered by overlays"],"tags":["browser","timeout","navigation","race-condition"],"backgroundTag":"wait-timeout","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}