spring-projects/spring-ai · error · McpTransportException

Error parsing JSON-RPC message:

Error message

Error parsing JSON-RPC message: 

What it means

In WebClientStreamableHttpTransport's SSE event processing, incoming SSE data is parsed with McpSchema.deserializeJsonRpcMessage. If that throws an IOException (malformed JSON or not a valid JSON-RPC structure), the parse step wraps it in McpTransportException with message "Error parsing JSON-RPC message: " + the raw data, failing the reactive stream and typically closing the transport.

Source

Thrown at mcp/transport/mcp-spring-webflux/src/main/java/org/springframework/ai/mcp/client/webflux/transport/WebClientStreamableHttpTransport.java:568

	private Tuple2<Optional<String>, Iterable<McpSchema.JSONRPCMessage>> parse(ServerSentEvent<String> event) {
		if (isMessageEvent(event.event())) {
			String data = event.data();
			if (data == null || data.isEmpty()) {
				// messages without `event: ` may be empty and should be ignored
				logger.debug("Ignoring SSE message event with empty data: %s".formatted(event));
				return Tuples.of(Optional.empty(), List.of());
			}
			try {
				// We don't support batching ATM and probably won't since the next version
				// considers removing it.
				McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(this.jsonMapper, data);
				String eventId = event.id();
				Optional<String> idOpt = (eventId != null) ? Optional.of(eventId) : Optional.empty();
				return Tuples.of(idOpt, List.of(message));
			}
			catch (IOException ioException) {
				throw new McpTransportException("Error parsing JSON-RPC message: " + data, ioException);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Received SSE event with type: " + event);
			}
			return Tuples.of(Optional.empty(), List.of());
		}
	}

	private static boolean isMessageEvent(@Nullable String eventType) {
		// Per SSE semantics, missing/blank event type defaults to "message".
		return !StringUtils.hasText(eventType) || MESSAGE_EVENT_TYPE.equals(eventType);
	}

	/**
	 * Builder for {@link WebClientStreamableHttpTransport}.
	 */

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Inspect the raw data in the exception message to see what the server actually sent; fix the sender or intermediary (proxy, gateway) to emit valid JSON-RPC over SSE.
  2. Verify both client and server agree on the MCP streamable-HTTP protocol version and JSON content type (application/json vs text/event-stream).
  3. Disable/bypass intermediaries that may rewrite responses (compression, buffering, HTML error pages) and retest.

Example fix

// server bug example: sending plain text over SSE
// before
sendEvent("data: ping\n\n");

// after
sendEvent("data: {\"jsonrpc\":\"2.0\",\"method\":\"notifications/message\",\"params\":{}}\n\n");
Defensive patterns

Strategy: try-catch

Validate before calling

// Client-side sanity check before trusting an SSE payload
if (data == null || !data.trim().startsWith("{")) {
    logger.warn("Skipping non-JSON SSE payload: " + data);
    return;
}

Try / catch

try {
    processSseEvent(event);
} catch (McpTransportException e) {
    if (e.getCause() instanceof IOException) {
        logger.error("Malformed JSON-RPC payload: " + e.getMessage());
        // reconnect with backoff
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: An SSE event body is not valid JSON, is JSON but not a JSON-RPC request/response/notification, or contains truncated/garbled data — deserializeJsonRpcMessage throws IOException, which parse() converts to McpTransportException.

Common situations: Proxy or load balancer injecting HTML error pages into the SSE stream; server sending non-JSON keep-alive or custom event payloads; compression/encoding mismatch corrupting the body; server-side serialization bugs or protocol-version mismatches.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/d7167540761453c0. Report an issue: GitHub.