-
Notifications
You must be signed in to change notification settings - Fork 2.5k
WebClientStreamableHttpTransport silently drops body-level errors #5775
Description
Description
WebClientStreamableHttpTransport.sendMessage() uses onErrorComplete to handle errors in the reactive chain that processes HTTP response bodies. When a body-level error occurs (e.g. DataBufferLimitException from oversized responses, malformed JSON, SSE parse errors), this operator silently completes the stream. The pending McpClientSession response is never resolved, causing the caller to hang until requestTimeout (typically 300 seconds).
This is the same bug as modelcontextprotocol/java-sdk#889, which was filed against the original WebClientStreamableHttpTransport before it was moved to Spring AI in java-sdk@77bc64a.
Root Cause
In sendMessage(), the reactive chain ends with:
.onErrorComplete(t -> {
this.handleException(t);
sink.error(t);
return true;
})onErrorComplete swallows the error after calling sink.error(t). While the sink is notified, any pending JSON-RPC response in McpClientSession.pendingResponses is never resolved because no message reaches the handler.
Fix
Change onErrorComplete to onErrorResume and emit a synthetic JSON-RPC error response for requests (not notifications), so McpClientSession resolves immediately:
.onErrorResume(t -> {
this.handleException(t);
sink.error(t);
if (requestId != null) {
McpSchema.JSONRPCResponse errorResponse = new McpSchema.JSONRPCResponse(
McpSchema.JSONRPC_VERSION, requestId, null,
new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
"Transport error during response streaming: " + t.getMessage(), null));
return this.handler.get().apply(Mono.just(errorResponse));
}
return Flux.empty();
})Related
- Upstream issue: modelcontextprotocol/java-sdk#889
- Fix for
HttpClientStreamableHttpTransport(java-sdk main): java-sdk#896 - Fix for both transports (java-sdk 0.17.x): java-sdk#897
- PR for this repo: Propagate body-level errors in WebClientStreamableHttpTransport #5774