{"record":{"id":"3d641302317e6336","repo":"ruvnet/ruflo","slug":"http-transport-failed-string-firsterror-instanc","errorCode":null,"errorMessage":"HTTP transport failed: ${String(firstError instanceof Error ? firstError.message : firstError)}; SSE fallback failed: ${String(err instanceof Error ? err.message : err)}","messagePattern":"HTTP transport failed: (.+?); SSE fallback failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ruflo/src/ruvocal/src/lib/server/mcp/clientPool.ts","lineNumber":208,"sourceCode":"\t\t\t\trecordFailure(key, 429, err);\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\terr instanceof Error ? err.message : String(err)\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (effectiveStatus !== undefined) {\n\t\t\t\trecordFailure(key, effectiveStatus, err);\n\t\t\t}\n\n\t\t\tconst message =\n\t\t\t\t\"HTTP transport failed: \" +\n\t\t\t\tString(firstError instanceof Error ? firstError.message : firstError) +\n\t\t\t\t\"; SSE fallback failed: \" +\n\t\t\t\tString(err instanceof Error ? err.message : err);\n\t\t\tthrow new Error(message, { cause: err instanceof Error ? err : undefined });\n\t\t}\n\t}\n\n\tpool.set(key, client);\n\treturn client;\n}\n\nexport async function drainPool() {\n\tfor (const [key, client] of pool) {\n\t\ttry {\n\t\t\tawait client.close?.();\n\t\t} catch {}\n\t\tpool.delete(key);\n\t}\n\tfailureCooldown.clear();\n}\n\nexport function evictFromPool(server: McpServerConfig): Client | undefined {","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/ruflo/src/ruvocal/src/lib/server/mcp/clientPool.ts#L190-L226","documentation":"Thrown by getClient when the StreamableHTTP transport failed with a recoverable error (network, 408, or transport mismatch - i.e. shouldSkipSseFallback returned false), the SSE fallback was attempted, and SSE also failed with a non-429 status. The combined Error concatenates both transport error messages and attaches the SSE error as cause. effectiveStatus, if defined, is recorded as a cooldown.","triggerScenarios":"Network unreachable to the MCP server host; wrong scheme (http vs https); self-signed cert; server only supports StreamableHTTP but the request triggered a transport mismatch that then failed over SSE which also errored; DNS resolution failure; timeout on both transports; proxy/firewall blocking both paths.","commonSituations":"Local dev with misconfigured MCP URL (e.g. wrong port); corporate proxy blocking SSE; MCP server that only implements the new StreamableHTTP transport returning an SSE-specific error on fallback; TLS/cert problems; containerized deployments where the MCP host is unreachable from the chat-ui pod.","solutions":["Read both halves of the message to localize the failure: 'HTTP transport failed: X; SSE fallback failed: Y'.","Verify network reachability and scheme from the chat-ui host: curl -i the MCP URL.","If the server only supports one transport, pin that transport or remove the redundant fallback path.","Inspect err.cause (the SSE error) for SDK-specific details such as certificate or DNS errors."],"exampleFix":"// before\nconst client = await getClient({ url: 'http://localhost:9999/mcp', name: 'svc' });\n\n// after\ntry {\n  const client = await getClient({ url: MCP_URL, name: 'svc' });\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('HTTP transport failed')) {\n    logger.error({ url: MCP_URL, err: e.message, cause: String(e.cause) }, 'MCP connect failed');\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"import { isURLLocal } from \"$lib/server/isURLLocal\";\nasync function preflightMcpUrl(url: string) {\n  let u: URL;\n  try { u = new URL(url); } catch { return { ok: false, reason: \"bad-url\" }; }\n  // basic reachability hint without consuming quota\n  return { ok: true, host: u.host, scheme: u.protocol };\n}","typeGuard":"function isDualTransportFailure(e: unknown): e is Error {\n  return e instanceof Error && e.message.startsWith(\"HTTP transport failed\");\n}","tryCatchPattern":"try { const client = await getClient(server); }\ncatch (e) {\n  if (e instanceof Error && e.message.startsWith(\"HTTP transport failed\")) {\n    logger.error({ url: server.url, msg: e.message, cause: String((e as Error & { cause?: unknown }).cause) }, \"MCP unreachable\");\n    return { reachable: false };\n  }\n  throw e;\n}","preventionTips":["Verify network reachability from the chat-ui host (curl -i the MCP URL) before wiring it into MCP_SERVERS.","Match the URL scheme to what the server implements (https for StreamableHTTP, http(s) for SSE).","Inspect err.cause for SDK-level detail (DNS, TLS, connection refused).","Avoid routing MCP traffic through proxies that block SSE long-polling."],"tags":["mcp","network","transport","sse","fallback","config"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}