{"record":{"id":"161c16e18df934bf","repo":"conductor-oss/conductor","slug":"http-d-error-from-mcp-server-s","errorCode":null,"errorMessage":"HTTP %d error from MCP server: %s","messagePattern":"HTTP (.+?) error from MCP server: (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java","lineNumber":128,"sourceCode":"            request.put(\"id\", 1);\n\n            Request.Builder requestBuilder =\n                    new Request.Builder()\n                            .url(serverUrl)\n                            .post(\n                                    RequestBody.create(\n                                            objectMapper.writeValueAsString(request),\n                                            MediaType.get(\"application/json\")))\n                            .header(\"Content-Type\", \"application/json\")\n                            .header(\"Accept\", \"application/json, text/event-stream\");\n\n            // Add custom headers\n            addHeaders(requestBuilder, headers);\n\n            ResponsePayload response = execute(requestBuilder.build());\n            // Check response status\n            if (response.statusCode < 200 || response.statusCode >= 300) {\n                throw new RuntimeException(\n                        String.format(\n                                \"HTTP %d error from MCP server: %s\",\n                                response.statusCode, response.body));\n            }\n\n            // Get response body and content type\n            String responseBody = response.body;\n            String contentType = response.contentType;\n\n            // Parse response based on content type\n            JsonNode responseJson;\n            if (contentType != null && contentType.contains(\"text/event-stream\")) {\n                // Parse SSE format\n                responseJson = parseSseResponse(responseBody);\n            } else {\n                // Parse as JSON directly\n                responseJson = objectMapper.readTree(responseBody);\n            }","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java#L110-L146","documentation":"Thrown by MCPService.listToolsDirectHttp when the remote MCP server returns an HTTP status outside the 200-299 success range for a tools/list JSON-RPC POST. The %d is the status code and %s is the raw response body (read and size-bounded first). It signals the server rejected the request at the transport layer before any JSON-RPC semantics apply.","triggerScenarios":"Calling MCPService.listTools(serverUrl, headers) against an MCP endpoint that responds 4xx/5xx (e.g. 401 Unauthorized when headers lack a valid token, 404 for a wrong URL, 500 from a crashed server, 413 if the request itself is large). Also a server that requires the full MCP initialize handshake and returns 400 to the raw JSON-RPC call this code makes.","commonSituations":"Wrong base URL (pointed at an HTML page or the SSE endpoint instead of the streamable-HTTP endpoint); missing or expired Authorization header; MCP server behind a gateway that returns 403; server that only accepts the initialized session and rejects bare tools/list.","solutions":["Verify serverUrl points at the MCP server's streamable-HTTP JSON-RPC endpoint, not a browser or SSE-only URL.","Confirm the headers map contains the auth the server expects (Bearer token, API key) and that it is not expired.","Reproduce with curl: POST the same tools/list JSON-RPC body with the same headers and inspect the status/body.","If the server requires MCP initialize, use a client/transport that performs the handshake instead of this raw-JSON-RPC path."],"exampleFix":"// before\nList<McpSchema.Tool> tools = mcpService.listTools(url, headers);\n// after\nif (headers == null || headers.get(\"Authorization\") == null) {\n    throw new IllegalStateException(\"MCP server requires Authorization; header missing\");\n}\nList<McpSchema.Tool> tools;\ntry {\n    tools = mcpService.listTools(url, headers);\n} catch (RuntimeException e) {\n    throw new RuntimeException(\"tools/list failed for \" + url + \" (check URL/auth): \" + e.getMessage(), e);\n}","handlingStrategy":"try-catch","validationCode":"// Validate the URL is a plausible MCP JSON-RPC endpoint before calling.\njava.net.URI u = java.net.URI.create(serverUrl);\nif (!u.getScheme().matches(\"https?\") || u.getHost() == null) {\n    throw new IllegalArgumentException(\"Invalid MCP server URL: \" + serverUrl);\n}","typeGuard":null,"tryCatchPattern":"try {\n    List<McpSchema.Tool> tools = mcpService.listTools(serverUrl, headers);\n} catch (RuntimeException e) {\n    // Message contains 'HTTP <code> error'; parse the code for retry/backoff decisions.\n    if (e.getMessage().contains(\"HTTP 5\") || e.getMessage().contains(\"HTTP 429\")) {\n        // transient: retry with backoff\n    } else {\n        throw e;\n    }\n}","preventionTips":["Keep serverUrl and credentials in configuration validated at startup, not per-call.","Distinguish 5xx/429 (retry) from 4xx (fix auth/URL) by parsing the embedded status code.","Log the raw response body present in the message for diagnosis."],"tags":["mcp","network","http-status","json-rpc","authentication"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}