{"record":{"id":"89e97adf1cb5c976","repo":"can1357/oh-my-pi","slug":"http-response-status-text","errorCode":null,"errorMessage":"HTTP ${response.status}: ${text}","messagePattern":"HTTP (.+?): (.+?)","errorType":"http","errorClass":"MCPTransportError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/mcp/transports/http.ts","lineNumber":788,"sourceCode":"\t\t}\n\n\t\tconst timeout = resolveMCPTimeoutMs(this.config.timeout);\n\t\tconst operation = createMCPTimeout(timeout, this.#operationSignal());\n\t\tlet stage: MCPFailureStage = \"send\";\n\t\tlet traceId: string | undefined;\n\n\t\ttry {\n\t\t\tconst response = await this.#fetch(\n\t\t\t\t{ method: \"POST\", body: JSON.stringify(body), signal: operation.signal },\n\t\t\t\tgenerated,\n\t\t\t);\n\t\t\tstage = \"receive\";\n\t\t\ttraceId = mcpTraceIdFromHeaders(response.headers);\n\n\t\t\t// 202 Accepted is success for notifications\n\t\t\tif (!response.ok && response.status !== 202) {\n\t\t\t\tconst text = await response.text();\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: \"http_status\",\n\t\t\t\t\tmessage: `HTTP ${response.status}: ${text}`,\n\t\t\t\t\tretryable: response.status === 404 || response.status === 502 || response.status === 503,\n\t\t\t\t\tcode: response.status,\n\t\t\t\t\ttraceId,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// The server may piggyback server-to-client requests or notifications\n\t\t\t// on the notification response (MCP Streamable HTTP spec). Read them.\n\t\t\tconst contentType = response.headers.get(\"Content-Type\") ?? \"\";\n\t\t\tif (contentType.includes(\"text/event-stream\") && response.body) {\n\t\t\t\t// Use the SSE connection's signal if available; otherwise keep the existing finite read timeout.\n\t\t\t\tif (this.#sseConnection) {\n\t\t\t\t\tthis.#trackBackgroundDrain(\n\t\t\t\t\t\tthis.#readSSEStream(response.body, this.#operationSignal(this.#sseConnection.signal)),","sourceCodeStart":770,"sourceCodeEnd":806,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/mcp/transports/http.ts#L770-L806","documentation":"The server answered a JSON-RPC POST (a notification here) with a non-2xx HTTP status other than the acceptable 202 Accepted. The transport surfaces the status code plus the response body text as an http_status MCPTransportError. Retryability is limited to 404, 502, and 503; other statuses (401/404-session-gone/500) are terminal. Because this path is the notification sender, the stage is 'receive' after the request was fully sent.","triggerScenarios":"401/403 from expired or missing auth credentials; 404 when the server restarted and the Mcp-Session-Id no longer exists; 500 from a server-side handler failure; 502/503 from an unavailable reverse proxy or overloaded backend.","commonSituations":"Auth tokens expiring mid-session; MCP server redeploy while the client holds an old session id; wrong endpoint URL (posting to the legacy SSE endpoint rather than the message endpoint); gateway rate limiting.","solutions":["Read the embedded body text for the server's error detail (often a JSON-RPC or auth error message).","For 401/403: refresh credentials or reconfigure auth before reconnecting.","For 404: the session was lost — reconnect (re-initialize) to get a fresh Mcp-Session-Id; the error is retryable for this reason.","For 502/503: retry with backoff, the backend is temporarily unavailable.","For 500: inspect the server logs; the response body text will point at the failing handler."],"exampleFix":"// client (before): keeps notifying on a dead session\nawait transport.notify('notifications/cancelled', { requestId });\n// after\ntry {\n  await transport.notify('notifications/cancelled', { requestId });\n} catch (e) {\n  if (e instanceof MCPTransportError && e.code === 404) {\n    await reconnect(); // fresh session, then resend\n  }\n}","handlingStrategy":"retry","validationCode":"// pre-flight: confirm endpoint reachable and credentials valid\nconst probe = await fetch(url, { method: 'HEAD' });\nif (probe.status === 401 || probe.status === 403) throw new Error('MCP credentials invalid/missing');\nif (probe.status === 404) throw new Error('Wrong MCP endpoint URL');","typeGuard":"function isHttpStatusError(e: unknown, codes: number[]): e is MCPTransportError & { code: number } {\n  return e instanceof MCPTransportError && e.failure === 'http_status' &&\n    typeof (e as { code?: number }).code === 'number' && codes.includes((e as { code: number }).code);\n}","tryCatchPattern":"try {\n  await transport.notify('notifications/cancelled', { requestId });\n} catch (e) {\n  if (e instanceof MCPTransportError && e.failure === 'http_status') {\n    if ([404, 502, 503].includes(e.code ?? 0)) await backoffRetry(() => reconnectAndNotify());\n    else if (e.code === 401) await refreshAuthAndReconnect();\n    else throw e; // 5xx server bug: surface to user\n  } else throw e;\n}","preventionTips":["Refresh auth tokens proactively before expiry.","Reconnect (re-initialize) after any server redeploy — session ids do not survive restarts.","Point notifications at the message endpoint, not the SSE/stream endpoint.","Log e.code and e.traceId with server correlation in mind."],"tags":["mcp","http","http-status","notification"],"backgroundTag":"http-error-response","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}