can1357/oh-my-pi · error · MCPTransportError

Request timeout after ${timeout}ms

Error message

Request timeout after ${timeout}ms

What it means

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.

Source

Thrown at packages/coding-agent/src/mcp/transports/http.ts:523

			if (result.error !== undefined) {
				if (
					!isRecord(result.error) ||
					typeof result.error.code !== "number" ||
					typeof result.error.message !== "string"
				) {
					throw new SyntaxError("Malformed JSON-RPC error response");
				}
				throw createMCPJsonRpcError(
					"http",
					{ code: result.error.code, message: result.error.message, data: result.error.data },
					traceId,
				);
			}

			return result.result as T;
		} catch (error) {
			if (operation.isTimeoutAbort(error) || operation.timedOut()) {
				throw new MCPTransportError({
					transport: "http",
					stage,
					failure: "timeout",
					message: `Request timeout after ${timeout}ms`,
					retryable: false,
					traceId,
					cause: error,
				});
			}
			if (error instanceof Error && error.name === "AbortError") throw error;
			throw normalizeMCPTransportError(error, { transport: "http", stage, traceId });
		} finally {
			operation.clear();
		}
	}

	#parseSSEResponse<T>(response: Response, expectedId: string | number, options?: MCPRequestOptions): Promise<T> {
		const traceId = mcpTraceIdFromHeaders(response.headers);

View on GitHub (pinned to 9690622007)

Solutions

  1. Raise the timeout in the transport config for slow tools/operations
  2. Use per-request options (MCPRequestOptions.signal/timeout) to extend timeout only for long-running calls
  3. Investigate server-side latency for the specific method (logs, tracing) — the timeout is a symptom
  4. If retries are safe (idempotent methods), implement retry at the caller with backoff, since the error is non-retryable internally
  5. Fix network path issues (black-holed connections, NAT timeouts) if latency is not server-side

Example fix

// before
const transport = new HttpTransport({ url, timeout: 5_000 }); // tools take ~30s
// after
const transport = new HttpTransport({ url, timeout: 60_000 });
// or per-request:
await transport.request("tools/call", { name: "research" }, { timeout: 120_000 });
Defensive patterns

Strategy: retry

Validate before calling

// Estimate a sane timeout by measuring a cheap method first:
const t0 = performance.now();
await transport.request("ping");
const rtt = performance.now() - t0;
const timeout = Math.max(30_000, rtt * 50);

Try / catch

try {
  return await transport.request(method, params, { signal });
} catch (err) {
  if (err instanceof MCPTransportError && err.failure === "timeout") {
    // only retry if the method is idempotent
    if (isIdempotent(method)) return retryWithBackoff();
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: Default timeout too small for expensive tools; server stalled on downstream dependencies; network black-hole dropping the response; load-induced latency spikes.

Understand the failure class

Related errors


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