{"record":{"id":"b6270584b4428f5a","repo":"ruvnet/ruflo","slug":"mcp-server-server-name-returned-http-httpst","errorCode":null,"errorMessage":"MCP server \"${server.name}\" returned HTTP ${httpStatus}: ${httpErr instanceof Error ? httpErr.message : String(httpErr)}","messagePattern":"MCP server \"(.+?)\" returned HTTP (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/server/mcp/clientPool.ts","lineNumber":164,"sourceCode":"\t\t// 4xx/5xx (except 408): falling back to SSE will hit the same upstream\n\t\t// and fail the same way. Surface a clean error with rate-limit info.\n\t\tif (shouldSkipSseFallback(httpStatus)) {\n\t\t\ttry {\n\t\t\t\tawait client.close?.();\n\t\t\t} catch {}\n\n\t\t\trecordFailure(key, httpStatus, httpErr);\n\n\t\t\tif (httpStatus === 429) {\n\t\t\t\tconst retryAfterMs = extractRetryAfterMs(httpErr);\n\t\t\t\tthrow new McpRateLimitedError(\n\t\t\t\t\tserver.name,\n\t\t\t\t\t429,\n\t\t\t\t\tretryAfterMs,\n\t\t\t\t\thttpErr instanceof Error ? httpErr.message : String(httpErr)\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new Error(\n\t\t\t\t`MCP server \"${server.name}\" returned HTTP ${httpStatus}: ` +\n\t\t\t\t\t(httpErr instanceof Error ? httpErr.message : String(httpErr)),\n\t\t\t\t{ cause: httpErr instanceof Error ? httpErr : undefined }\n\t\t\t);\n\t\t}\n\n\t\t// Recoverable failure (network, 408, transport mismatch) — try SSE fallback.\n\t\ttry {\n\t\t\tawait client.connect(new SSEClientTransport(url, { requestInit }));\n\t\t} catch (err) {\n\t\t\ttry {\n\t\t\t\tawait client.close?.();\n\t\t\t} catch {}\n\n\t\t\t// Combine both errors for the caller. Honor 429 status if either side\n\t\t\t// surfaced a rate-limit (the upstream is rate-limiting independent of\n\t\t\t// transport).\n\t\t\tconst sseStatus = statusFromTransportError(err);","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/ruflo/src/ruvocal/src/lib/server/mcp/clientPool.ts#L146-L182","documentation":"During Streamable HTTP connect, a definitive non-429 4xx/5xx (per shouldSkipSseFallback: any 400–599 except 408) skips the SSE fallback and throws this error (clientPool.ts:164), embedding the HTTP status, the SDK error message, and the original error as cause. The failure is also recorded, triggering the cooldown map for subsequent attempts.","triggerScenarios":"MCP server URL answering 401 (missing/wrong auth headers), 403 (forbidden key), 404 (wrong path — e.g. forgot the /mcp suffix), or 5xx on the initialize POST; thrown on the first connect for that url+headers key.","commonSituations":"MCP_SERVERS env entry with a typo'd path; token expired or lacking scopes; reverse proxy (nginx/traefik) misrouting /mcp; upstream deployed a breaking change returning 500 on initialize.","solutions":["Reproduce outside the app: curl -v -X POST <url> with the same headers to see the raw status","Fix the URL (usually append /mcp) or the auth headers in MCP_SERVERS","For 5xx, check upstream server logs/proxy health","After fixing, call evictFromPool(server) or wait ~5s so the recorded cooldown does not mask the fix"],"exampleFix":"# before (config)\nMCP_SERVERS=[{\"name\":\"tools\",\"url\":\"https://mcp.example.com\"}]  # 404 -> HTTP status error\n\n# after\nMCP_SERVERS=[{\"name\":\"tools\",\"url\":\"https://mcp.example.com/mcp\"}]","handlingStrategy":"fallback","validationCode":"// smoke-test the entry before adding it to MCP_SERVERS\nasync function probeMcpEntry(url: string, headers: Record<string, string>) {\n\tconst res = await fetch(url, { method: \"POST\", headers });\n\tif (res.status >= 400) throw new Error(`MCP probe failed: HTTP ${res.status}`);\n}","typeGuard":"function isMcpHttpStatusError(e: unknown): boolean {\n\treturn e instanceof Error && /MCP server \".+\" returned HTTP \\d{3}/.test(e.message);\n}","tryCatchPattern":"try {\n\tclient = await getClient(server);\n} catch (e) {\n\tif (isMcpHttpStatusError(e)) {\n\t\t// definitive upstream rejection: skip this server, keep session alive without its tools\n\t\tlogger.warn({ err: e }, `[mcp] disabling server ${server.name}`);\n\t\treturn null;\n\t}\n\tthrow e;\n}","preventionTips":["Validate MCP_SERVERS entries with a curl/fetch probe at deploy time","Keep auth headers and the /mcp path in the config source-controlled and reviewed","Treat 401/404 as config bugs — alert, don't retry"],"tags":["mcp","http-status","connection","auth","configuration"],"backgroundTag":"upstream-http-error","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}