Skip to content

batch_requests masks the server's error when a JSON-RPC batch is rejected as a whole #152

Description

@Yoni-Starkware

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    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