nautechsystems/nautilus_trader · error

Failed to parse eth_call response

Error message

Failed to parse eth_call response

What it means

This error is raised in `call_result_at` when the HTTP body returned by the node for an `eth_call` cannot be deserialized into the expected `RpcNodeHttpResponse<String>` JSON-RPC envelope. The library expects either a JSON-RPC 2.0 response (with `jsonrpc`/`result`) or a non-standard `{code, message}` error object; anything else (HTML, empty body, truncated JSON, proxy error pages) triggers it. It wraps the raw transport layer so callers get a single anyhow error for the eth_call path.

Source

Thrown at crates/adapters/blockchain/src/rpc/http.rs:545

        let request = serde_json::json!({
            "jsonrpc": "2.0",
            "id": 1,
            "method": "eth_call",
            "params": [call, block_parameter(Some(block))],
        });
        let bytes = self
            .send_rpc_request(request, Some(EXECUTION_RPC_TIMEOUT_SECS))
            .await
            .map_err(|e| match e {
                BlockchainRpcClientError::ClientError(message)
                    if message.contains("redirect response rejected") =>
                {
                    anyhow::anyhow!("eth_call redirect rejected")
                }
                _ => anyhow::anyhow!("eth_call request failed"),
            })?;
        let parsed = serde_json::from_slice::<RpcNodeHttpResponse<String>>(bytes.as_ref())
            .map_err(|_| anyhow::anyhow!("Failed to parse eth_call response"))?;

        if parsed.jsonrpc.is_none()
            && let (Some(code), Some(message)) = (parsed.code, parsed.message)
        {
            if eth_call_error_is_revert(code, &message) {
                return Ok(RpcCallResult::Reverted);
            }
            anyhow::bail!("eth_call RPC error {code}");
        }

        if let Some(error) = parsed.error {
            if eth_call_error_is_revert(error.code, &error.message) {
                return Ok(RpcCallResult::Reverted);
            }
            anyhow::bail!("eth_call RPC error {}", error.code);
        }
        let value = parsed
            .result

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the execution RPC URL points at a JSON-RPC endpoint (curl -X POST with an eth_chainId request) and not a dashboard or proxy page
  2. Inspect the raw response body (curl the same eth_call) to see whether the node returns HTML or malformed JSON
  3. Retry against a healthy node or another provider; persistent failures indicate node/proxy issues
  4. Upgrade the adapter/node if the node uses a non-standard JSON-RPC response shape

Example fix

// before: endpoint returns HTML on error, causing parse failure
rpc_url = "https://my-lb.example.com/eth"  // LB returns HTML 502
// after: point directly at a JSON-RPC endpoint and pre-check health
rpc_url = "https://node.example.com:8545"
// curl -X POST $rpc_url -d '{"jsonrpc":"2.0","method":"eth_chainId","id":1}'
Defensive patterns

Strategy: try-catch

Validate before calling

async fn rpc_endpoint_ok(url: &str) -> bool {
    let body = reqwest::Client::new().post(url)
        .json(&serde_json::json!({"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}))
        .send().await.ok()?.text().await.ok()?;
    body.trim_start().starts_with('{') && serde_json::from_str::<serde_json::Value>(&body).is_ok()
}

Type guard

fn is_json_rpc_envelope(v: &serde_json::Value) -> bool {
    v.is_object() && (v.get("jsonrpc").is_some() || v.get("result").is_some() || v.get("error").is_some())
}

Try / catch

match client.call_at(&tx, block).await {
    Ok(res) => handle(res),
    Err(e) if e.to_string().contains("Failed to parse eth_call response") => {
        log::warn!("non-JSON RPC body; failing over");
        fallback_client.call_at(&tx, block).await
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: The `eth_call` HTTP response body is not valid JSON, or is valid JSON that does not match the `RpcNodeHttpResponse<String>` shape (missing both `jsonrpc` and `result`/`code`/`message` fields), e.g. an HTML error page from a reverse proxy, a rate-limit text body, or a non-standard node response.

Common situations: Pointing the execution RPC URL at a load balancer or Cloudflare endpoint that returns HTML 502/429 pages; node returning chunked/truncated responses under load; misconfigured port hitting a web UI instead of the JSON-RPC endpoint.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/19410fecc572ed18. Report an issue: GitHub.