You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
WebClientStreamableHttpTransport: SSE body errors silently dropped, callTool hangs until timeout #889
When an MCP server returns an SSE response whose body exceeds the Spring WebClient maxInMemorySize buffer limit (default 256KB), the DataBufferLimitException is silently dropped and the callTool() call hangs until the request timeout expires. The caller receives a TimeoutException instead of a meaningful error.
Root Cause
In WebClientStreamableHttpTransport.sendMessage(), for SSE responses, sink.success() is called as soon as the SSE headers arrive — before the body is read:
elseif (mediaType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM)) {
logger.debug("Established SSE stream via POST");
sink.success(); // <-- signals success before body is readreturnnewEventStream(response, sessionRepresentation);
}
When the body reading subsequently fails (e.g., DataBufferLimitException from an oversized response), the error flows through:
sendMessage().onErrorComplete() handler → calls handleException(t) then sink.error(t)
sink.error(t) is a no-op because sink.success() was already called
The pendingResponses entry in McpClientSession is orphaned — no response or error is ever delivered. The callTool() Mono waits indefinitely until requestTimeout.
Note: the same early sink.success() pattern also exists for JSON (APPLICATION_JSON) responses in the same method, so the issue may affect non-SSE responses as well.
Reproduction
Configure a WebClient with a small maxInMemorySize (e.g., 256KB default)
Have an MCP server tool return a response larger than that limit
Call the tool via McpSyncClient.callTool()
Observe: the call hangs for the full requestTimeout duration, then throws TimeoutException
Expected Behavior
The callTool() call should fail promptly with a meaningful error (e.g., wrapping the DataBufferLimitException) instead of hanging until timeout.
Suggested Fix
Either:
Don't call sink.success() until the body is fully consumed, or
Propagate body-reading errors to the pendingResponses sink in McpClientSession via a separate error path (since the MonoSink is already terminated)
Activity
Propagate body-level errors as synthetic JSON-RPC responses
Propagate body-level errors as synthetic JSON-RPC responses
Propagate body-level errors as synthetic JSON-RPC responses
Propagate body-level errors as synthetic JSON-RPC responses
Planview-JamesK commentedon Apr 2, 2026
Here is one possible fix: #896
Propagate body-level errors as synthetic JSON-RPC responses
Propagate body-level errors as synthetic JSON-RPC responses
Propagate body-level errors as synthetic JSON-RPC responses
tzolov commentedon Aug 17, 2026
WebClientStreamableHttpTransport no longer exists in this repo — the Spring transports moved out. Redirect to Spring AI