Skip to content

reqwest streamable HTTP client drops JSON-RPC error bodies on HTTP 4xx into TransportSend #724

@adam-r-kowalski

Description

@adam-r-kowalski

Describe the bug
A POST over Streamable HTTP that returns HTTP 4xx with Content-Type: application/json and a valid JSON-RPC error body is currently surfaced by the reqwest client as a transport error (UnexpectedServerResponse -> ServiceError::TransportSend) instead of a parsed JSON-RPC error (ServiceError::McpError).

This was reported in PR comment thread:
#709 (comment)

To Reproduce
Steps to reproduce the behavior:

  1. Use the current main behavior in crates/rmcp/src/transport/common/reqwest/streamable_http_client.rs where non-2xx is handled before JSON parsing.
  2. Send a request to a server that responds with:
    • status: 400 Bad Request
    • header: Content-Type: application/json
    • body: {"jsonrpc":"2.0","id":1,"error":{"code":-32602,"message":"Unknown prompt: example_prompt"}}
  3. Observe the response is returned as transport error instead of JSON-RPC error object.

Regression test I added for this behavior:

  • crates/rmcp/tests/test_streamable_http_http_error_jsonrpc.rs

Command used:

cargo test -p rmcp --test test_streamable_http_http_error_jsonrpc --features "client server transport-streamable-http-client-reqwest transport-streamable-http-server"

Expected behavior
If response body is valid JSON-RPC and Content-Type is application/json, it should be parsed and surfaced as JSON-RPC message (so request callers receive ServiceError::McpError for error responses), even when HTTP status is 4xx.

This appears consistent with Streamable HTTP spec language that for non-accepted inputs, server may return HTTP error status and response body may be JSON-RPC error response:

Logs
Observed error shape from failing regression expectation before fix:

Err(UnexpectedServerResponse("HTTP 400 Bad Request: {\"jsonrpc\":\"2.0\",\"id\":1,\"error\":{\"code\":-32602,\"message\":\"Unknown prompt: example_prompt\"}}"))

Additional context

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is not working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions