conductor-oss/conductor · error · RuntimeException

Invalid JSON-RPC response: missing 'result' field

Error message

Invalid JSON-RPC response: missing 'result' field

What it means

Thrown by listToolsDirectHttp when the JSON-RPC response has neither an `error` nor a `result` field. Per JSON-RPC 2.0 a valid response to a request must contain exactly one of result/error; absence of both means the server returned a structurally non-conformant payload (e.g. just an ack, or an SSE event that was not a JSON-RPC response).

Solutions

  1. Capture the raw response body to confirm what the server actually returned (it was logged at debug).
  2. Verify the endpoint is a JSON-RPC responder and not an event-stream info endpoint.
  3. Ensure the Accept header negotiated a transport the server uses to send the JSON-RPC result back.
  4. If the server only speaks SSE, confirm content-type detection worked so parseSseResponse was invoked.
Defensive patterns

Strategy: try-catch

Try / catch

try {
    mcpService.listTools(serverUrl, headers);
} catch (RuntimeException e) {
    if (e.getMessage().contains("missing 'result' field")) {
        // server returned a non-JSON-RPC envelope; log body and check endpoint/transport
    }
    throw e;
}

Prevention

When it happens

Trigger: Server returns `{"jsonrpc":"2.0","id":1}` with no result/error; server returns a notification-style or progress message as the response; SSE stream delivered only keep-alive/event frames and no data payload that resolved to a JSON-RPC response object.

Common situations: Hitting a server that streams server-sent events where the first non-data frames were concatenated; pointing at an endpoint that returns metadata instead of a JSON-RPC response; mismatched transport expectation (server is SSE-only but content-type was not text/event-stream).

Understand the failure class

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/dd68b2c55a271035. Report an issue: GitHub.

Appendix: source

Thrown at ai/src/main/java/org/conductoross/conductor/ai/mcp/MCPService.java:154

            String contentType = response.contentType;

            // Parse response based on content type
            JsonNode responseJson;
            if (contentType != null && contentType.contains("text/event-stream")) {
                // Parse SSE format
                responseJson = parseSseResponse(responseBody);
            } else {
                // Parse as JSON directly
                responseJson = objectMapper.readTree(responseBody);
            }

            if (responseJson.has("error")) {
                throw new RuntimeException(
                        "JSON-RPC error: " + responseJson.get("error").toString());
            }

            if (!responseJson.has("result")) {
                throw new RuntimeException("Invalid JSON-RPC response: missing 'result' field");
            }

            JsonNode result = responseJson.get("result");
            JsonNode toolsNode = result.get("tools");

            if (toolsNode == null || !toolsNode.isArray()) {
                throw new RuntimeException(
                        "Invalid response: 'tools' field is missing or not an array");
            }
            ExternalDataLimits.validateStructure(objectMapper.convertValue(result, Object.class));

            // Convert to McpSchema.Tool list
            // Use Jackson to deserialize tools directly (same pattern as stdio implementation)
            List<McpSchema.Tool> tools =
                    objectMapper.convertValue(
                            toolsNode,
                            objectMapper
                                    .getTypeFactory()

View on GitHub (pinned to cf7c3e4a8a)