Summary
When a JSON-RPC server rejects a batch request as a whole and returns a single top-level error object (valid per the JSON-RPC 2.0 spec), JsonRpcClient::batch_requests fails with an opaque invalid type: map, expected a sequence at line 1 column 0 and discards the server's actual error message. The caller has no way to learn why the batch failed.
Affected code
starknet-rust-providers/src/jsonrpc/transports/http.rs, HttpTransport::send_requests (the batch path) deserializes the response body unconditionally as a sequence:
// send_requests
let parsed_response: Vec<JsonRpcResponse<serde_json::Value>> =
serde_json::from_str(&response_body).map_err(Self::Error::Json)?;
The single-request path (send_request) deserializes into JsonRpcResponse<R> — an enum with Success/Error variants — so it surfaces server errors fine. The batch path has no equivalent handling for a non-array (single object) response.
Why this happens
Per the JSON-RPC 2.0 spec: "If the batch rpc call itself fails to be recognized as valid JSON or as an Array with at least one value, the response from the Server MUST be a single Response object." So a server returning a single error object for a batch is spec-compliant; the client must handle it.
Reproduction
Pathfinder caps JSON-RPC batches at 100 requests (jsonrpsee default). A batch of 101 returns a single error object instead of an array:
$ curl -s -X POST -H 'content-type: application/json' \
-d '[ <101 valid requests> ]' http://<pathfinder>/rpc/v0_8
{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error",
"data":{"reason":"array exceeds maximum length 100 at line 1 column 6658"}}}
Via batch_requests, the caller only sees:
invalid type: map, expected a sequence at line 1 column 0
The actionable reason (array exceeds maximum length 100) is present in the response body but is never surfaced.
Expected
The server's error (code / message / data) should be surfaced — as the single-request path already does — instead of being replaced by a generic serde type-mismatch error.
Suggested fix
In send_requests, if deserializing into Vec<JsonRpcResponse<_>> fails, fall back to parsing the body as a single JsonRpcResponse<serde_json::Value>; if it deserializes to an Error, surface that error. This likely needs a transport/provider error variant to carry a batch-level JSON-RPC error, since HttpTransportError has no such variant today.
Environment
- Crate:
starknet-rust-providers
- Present on
master HEAD (send_requests in http.rs) and pinned rev 7caedfe.
- Observed against Pathfinder v0.22.7, but applies to any node that returns a single error object for a rejected/oversized batch.
Summary
When a JSON-RPC server rejects a batch request as a whole and returns a single top-level error object (valid per the JSON-RPC 2.0 spec),
JsonRpcClient::batch_requestsfails with an opaqueinvalid type: map, expected a sequence at line 1 column 0and discards the server's actual error message. The caller has no way to learn why the batch failed.Affected code
starknet-rust-providers/src/jsonrpc/transports/http.rs,HttpTransport::send_requests(the batch path) deserializes the response body unconditionally as a sequence:The single-request path (
send_request) deserializes intoJsonRpcResponse<R>— an enum withSuccess/Errorvariants — so it surfaces server errors fine. The batch path has no equivalent handling for a non-array (single object) response.Why this happens
Per the JSON-RPC 2.0 spec: "If the batch rpc call itself fails to be recognized as valid JSON or as an Array with at least one value, the response from the Server MUST be a single Response object." So a server returning a single error object for a batch is spec-compliant; the client must handle it.
Reproduction
Pathfinder caps JSON-RPC batches at 100 requests (jsonrpsee default). A batch of 101 returns a single error object instead of an array:
Via
batch_requests, the caller only sees:The actionable reason (
array exceeds maximum length 100) is present in the response body but is never surfaced.Expected
The server's error (
code/message/data) should be surfaced — as the single-request path already does — instead of being replaced by a generic serde type-mismatch error.Suggested fix
In
send_requests, if deserializing intoVec<JsonRpcResponse<_>>fails, fall back to parsing the body as a singleJsonRpcResponse<serde_json::Value>; if it deserializes to anError, surface that error. This likely needs a transport/provider error variant to carry a batch-level JSON-RPC error, sinceHttpTransportErrorhas no such variant today.Environment
starknet-rust-providersmasterHEAD (send_requestsinhttp.rs) and pinned rev7caedfe.