{"record":{"id":"bfa7906f4ac80594","repo":"can1357/oh-my-pi","slug":"tab-waitforresponse-timed-out-after-timeoutms","errorCode":null,"errorMessage":"tab.waitForResponse() timed out after ${timeoutMs}ms","messagePattern":"tab\\.waitForResponse\\(\\) timed out after (.+?)ms","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts","lineNumber":748,"sourceCode":"\t): Promise<CmuxResponse> {\n\t\tconst timeoutMs = opts?.timeout ?? this.#runContext?.timeoutMs ?? 30_000;\n\t\tconst signal = this.#runContext?.signal;\n\t\tawait this.#installResponseObserver();\n\t\tconst startId = await this.#responseCursor();\n\t\tconst deadline = Date.now() + timeoutMs;\n\t\twhile (Date.now() <= deadline) {\n\t\t\tconst records = await this.#responseRecordsAfter(startId);\n\t\t\tfor (const record of records) {\n\t\t\t\tconst response = new CmuxResponse(record);\n\t\t\t\tif (typeof pattern === \"function\") {\n\t\t\t\t\tif (await pattern(response)) return response;\n\t\t\t\t} else if (pattern instanceof RegExp ? pattern.test(record.url) : record.url.includes(pattern)) {\n\t\t\t\t\treturn response;\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait untilAborted(signal, () => Bun.sleep(100));\n\t\t}\n\t\tthrow new ToolError(`tab.waitForResponse() timed out after ${timeoutMs}ms`);\n\t}\n\n\tasync id(id: number): Promise<CmuxElementHandle> {\n\t\tconst ref = this.#elementRefs.get(id)?.ref ?? `@e${id}`;\n\t\tawait this.#waitForSelector(ref, this.#runContext?.timeoutMs ?? 30_000);\n\t\treturn new CmuxElementHandle(this, ref);\n\t}\n\n\tensureRuntime(session: SessionSnapshot): JsRuntime {\n\t\tif (!this.#runtime) {\n\t\t\tthis.#runtime = new JsRuntime({\n\t\t\t\tinitialCwd: session.cwd,\n\t\t\t\tsessionId: `cmux-tab-${this.#surfaceId}`,\n\t\t\t});\n\t\t}\n\t\treturn this.#runtime;\n\t}\n","sourceCodeStart":730,"sourceCodeEnd":766,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/browser/cmux/cmux-tab.ts#L730-L766","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: anchored regex misses query params, wait started after click\nawait tab.click(\"#save\");\nawait tab.waitForResponse(/^https:\\/\\/app\\.example\\.com\\/api\\/save$/);\n// after: arm first, substring match, longer timeout\nconst res = tab.waitForResponse(\"/api/save\", { timeout: 60_000 });\nawait tab.click(\"#save\");\nawait res;","handlingStrategy":"retry","validationCode":"// ensure a matching request will be possible: arm before the action\nconst pending = tab.waitForResponse(\"/api/save\", { timeout: 30_000 });\n// ...triggering click happens here...","typeGuard":null,"tryCatchPattern":"const pending = tab.waitForResponse(\"/api/save\", { timeout: 60_000 });\nawait tab.click(\"#save\");\ntry {\n  const res = await pending;\n} catch (err) {\n  if (err instanceof ToolError && err.message.includes(\"waitForResponse() timed out\")) {\n    throw new Error(\"save request never fired — check that #save click triggered it\");\n  }\n  throw err;\n}","preventionTips":["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)"],"tags":["browser","network","timeout","polling"],"backgroundTag":"wait-timeout","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}