nautechsystems/nautilus_trader · error

eth_call RPC error {}

Error message

eth_call RPC error {}

What it means

Raised by `call_result_at` when the RPC response contains a structured `error` object whose code/message is not recognized as a revert. Like error 561 but for the standard `error` field of the JSON-RPC response; the error code is embedded in the message to help identify the node-side failure.

Source

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

                _ => 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
            .ok_or_else(|| anyhow::anyhow!("eth_call returned no result"))?;
        let stripped = value.strip_prefix("0x").unwrap_or(&value);
        let bytes = hex::decode(stripped)
            .map_err(|_| anyhow::anyhow!("Failed to decode eth_call response"))?;
        Ok(RpcCallResult::Success(Bytes::from(bytes)))
    }

    /// Estimates the gas required for a transaction via `eth_estimateGas`.
    ///
    /// # Errors
    ///
    /// Returns an error if the RPC call fails (including node-side revert of the simulated
    /// transaction) or the result is missing or malformed.
    pub async fn estimate_gas(
        &self,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the embedded error code: -32000 'header not found' → use an archive node or a lower block.
  2. Raise the gas limit/price if the error mentions gas.
  3. Validate parameters passed into the call (address checksums, block encoding) for -32602.
  4. Retry against a different RPC endpoint for -32603 internal errors.
Defensive patterns

Strategy: retry

Validate before calling

// Ensure the block is within node retention
let latest: u64 = provider.request("eth_blockNumber", ()).await?;
anyhow::ensure!(block <= latest.saturating_sub(REORG_MARGIN), "block too old for non-archive node");

Type guard

fn is_gas_error(err: &anyhow::Error) -> bool {
    let s = err.to_string();
    s.contains("gas required exceeds allowance") || s.contains("out of gas")
}

Try / catch

match rpc.call_at(from, to, value, data, block).await {
    Err(e) if is_gas_error(&e) => retry_with_higher_gas(),
    Err(e) if e.to_string().contains("header not found") => switch_to_archive_endpoint(),
    other => other,
}

Prevention

When it happens

Trigger: `eth_call` rejected with a structured non-revert error: -32000 execution errors not containing 'revert' (e.g. 'gas required exceeds allowance', 'header not found'), -32603 internal error, -32602 invalid params.

Common situations: Querying a block beyond the node's retention ('header not found' on non-archive nodes); gas limit exceeded for complex calls; malformed request params; provider internal errors during high load.

Related errors


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