{"record":{"id":"ffab4f872aa2e4d8","repo":"can1357/oh-my-pi","slug":"mcp-request-failed-response-status-response","errorCode":null,"errorMessage":"MCP request failed: ${response.status} ${response.statusText}","messagePattern":"MCP request failed: (.+?) (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/mcp/json-rpc.ts","lineNumber":106,"sourceCode":"\t\tid: Math.random().toString(36).slice(2),\n\t\tmethod,\n\t\tparams: params ?? {},\n\t};\n\n\tconst response = await fetch(url, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t\tAccept: \"application/json, text/event-stream\",\n\t\t},\n\t\tbody: JSON.stringify(body),\n\t\tsignal: options?.signal ?? AbortSignal.timeout(MCP_DEFAULT_TIMEOUT_MS),\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorMsg = `MCP request failed: ${response.status} ${response.statusText}`;\n\t\tlogger.error(errorMsg, { url: redactUrlForLog(url), method, params });\n\t\tthrow new Error(errorMsg);\n\t}\n\n\tconst text = await response.text();\n\tconst result = parseSSE(text) as JsonRpcResponse<T> | null;\n\n\tif (!result) {\n\t\tlogger.error(\"Failed to parse MCP response\", {\n\t\t\turl: redactUrlForLog(url),\n\t\t\tmethod,\n\t\t\tresponseText: text.slice(0, 500),\n\t\t});\n\t\tthrow new Error(\"Failed to parse MCP response\");\n\t}\n\n\treturn result;\n}\n","sourceCodeStart":88,"sourceCodeEnd":123,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/mcp/json-rpc.ts#L88-L123","documentation":"callMCP() sends a JSON-RPC request (initialize, tools/list, tools/call, etc.) over HTTP/SSE to an MCP server and throws this error when the HTTP response status is not OK (response.ok false). The message carries the numeric status and statusText; the URL is redacted before logging. It surfaces transport-level problems before any JSON-RPC parsing happens.","triggerScenarios":"Any callMCP() request to an http/sse MCP server where the server (or an intermediate proxy/gateway) returns a non-2xx status: 401/403 (missing or expired OAuth token), 404 (wrong path), 429 (rate limited), 5xx (server crash), or DNS/proxy errors surfaced as gateway responses.","commonSituations":"OAuth token expired and refresh hasn't run (401); server endpoint URL changed or includes a wrong base path (404); remote MCP provider rate-limiting (429); server down or deploying (502/503); corporate proxy rejecting the request.","solutions":["Read the status code: 401/403 -> re-authenticate with /mcp reauth or re-run OAuth; 404 -> fix the URL; 429 -> back off and retry later; 5xx -> check server health.","Verify the server's URL in the MCP config (correct host, port, and path).","For OAuth servers, clear stale credentials and re-authorize.","Retry transient statuses (429, 500, 502, 503) with exponential backoff.","Check the omp log file for the logged request details (url is redacted, method and params included)."],"exampleFix":"// before\nconst tools = await manager.listTools(\"remote\"); // throws on 401\n// after\ntry {\n  const tools = await manager.listTools(\"remote\");\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"401\")) {\n    await reauthorizeServer(\"remote\");\n  }\n  throw e;\n}","handlingStrategy":"retry","validationCode":"// Pre-flight reachability check before relying on the server\nconst res = await fetch(serverUrl, { method: \"HEAD\", signal: AbortSignal.timeout(5000) });\nif (!res.ok && res.status !== 405) throw new Error(`MCP endpoint unhealthy: ${res.status}`);","typeGuard":"function isTransientHttpFailure(message: string): boolean {\n  return /MCP request failed: (429|500|502|503|504)\\b/.test(message);\n}","tryCatchPattern":"try {\n  return await callMCP(url, method, params);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"MCP request failed:\")) {\n    if (isTransientHttpFailure(e.message)) return withRetry(() => callMCP(url, method, params));\n    if (/MCP request failed: 40[13]/.test(e.message)) await reauthorize(url); // then retry once\n  }\n  throw e;\n}","preventionTips":["Handle 401 proactively: refresh OAuth tokens before they expire.","Pin and verify endpoint URLs; test them with curl after provider changes.","Add exponential backoff for 429/5xx instead of immediate hard failure.","Monitor omp logs for recurring non-2xx statuses per server."],"tags":["mcp","http","network","json-rpc"],"backgroundTag":"http-request-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}