{"record":{"id":"7d254251615a2f3b","repo":"can1357/oh-my-pi","slug":"request-timeout-after-timeout-ms","errorCode":null,"errorMessage":"Request timeout after ${timeout}ms","messagePattern":"Request timeout after (.+?)ms","errorType":"exception","errorClass":"MCPTransportError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/mcp/transports/http.ts","lineNumber":523,"sourceCode":"\t\t\tif (result.error !== undefined) {\n\t\t\t\tif (\n\t\t\t\t\t!isRecord(result.error) ||\n\t\t\t\t\ttypeof result.error.code !== \"number\" ||\n\t\t\t\t\ttypeof result.error.message !== \"string\"\n\t\t\t\t) {\n\t\t\t\t\tthrow new SyntaxError(\"Malformed JSON-RPC error response\");\n\t\t\t\t}\n\t\t\t\tthrow createMCPJsonRpcError(\n\t\t\t\t\t\"http\",\n\t\t\t\t\t{ code: result.error.code, message: result.error.message, data: result.error.data },\n\t\t\t\t\ttraceId,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn result.result as T;\n\t\t} catch (error) {\n\t\t\tif (operation.isTimeoutAbort(error) || operation.timedOut()) {\n\t\t\t\tthrow new MCPTransportError({\n\t\t\t\t\ttransport: \"http\",\n\t\t\t\t\tstage,\n\t\t\t\t\tfailure: \"timeout\",\n\t\t\t\t\tmessage: `Request timeout after ${timeout}ms`,\n\t\t\t\t\tretryable: false,\n\t\t\t\t\ttraceId,\n\t\t\t\t\tcause: error,\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (error instanceof Error && error.name === \"AbortError\") throw error;\n\t\t\tthrow normalizeMCPTransportError(error, { transport: \"http\", stage, traceId });\n\t\t} finally {\n\t\t\toperation.clear();\n\t\t}\n\t}\n\n\t#parseSSEResponse<T>(response: Response, expectedId: string | number, options?: MCPRequestOptions): Promise<T> {\n\t\tconst traceId = mcpTraceIdFromHeaders(response.headers);","sourceCodeStart":505,"sourceCodeEnd":541,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/mcp/transports/http.ts#L505-L541","documentation":"The JSON-RPC request exceeded its configured timeout: the abort signal fired or the timeout operation registered expiry before a response completed. The transport converts any timeout-abort into an MCPTransportError with failure: timeout, naming the elapsed milliseconds; it is not retryable by default since the server may still be executing.","triggerScenarios":"Server takes longer than resolveMCPTimeoutMs(config.timeout) to answer — slow tool execution, long-running LLM-backed tools, hung upstream, or a too-tight client timeout for a legitimately slow operation.","commonSituations":"Default timeout too small for expensive tools; server stalled on downstream dependencies; network black-hole dropping the response; load-induced latency spikes.","solutions":["Raise the timeout in the transport config for slow tools/operations","Use per-request options (MCPRequestOptions.signal/timeout) to extend timeout only for long-running calls","Investigate server-side latency for the specific method (logs, tracing) — the timeout is a symptom","If retries are safe (idempotent methods), implement retry at the caller with backoff, since the error is non-retryable internally","Fix network path issues (black-holed connections, NAT timeouts) if latency is not server-side"],"exampleFix":"// before\nconst transport = new HttpTransport({ url, timeout: 5_000 }); // tools take ~30s\n// after\nconst transport = new HttpTransport({ url, timeout: 60_000 });\n// or per-request:\nawait transport.request(\"tools/call\", { name: \"research\" }, { timeout: 120_000 });","handlingStrategy":"retry","validationCode":"// Estimate a sane timeout by measuring a cheap method first:\nconst t0 = performance.now();\nawait transport.request(\"ping\");\nconst rtt = performance.now() - t0;\nconst timeout = Math.max(30_000, rtt * 50);","typeGuard":null,"tryCatchPattern":"try {\n  return await transport.request(method, params, { signal });\n} catch (err) {\n  if (err instanceof MCPTransportError && err.failure === \"timeout\") {\n    // only retry if the method is idempotent\n    if (isIdempotent(method)) return retryWithBackoff();\n  }\n  throw err;\n}","preventionTips":["Size timeouts to the slowest expected tool, not the fastest","Use per-request timeouts for known-long operations","Only retry idempotent methods — the server may have executed the call","Monitor server latency and alert before the client timeout binds","Exclude the operation from network paths that black-hole responses"],"tags":["mcp","timeout","http","latency"],"backgroundTag":"request-timeout","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}