nautechsystems/nautilus_trader · error

RPC error {}: {}

Error message

RPC error {}: {}

What it means

The RPC node returned a standard JSON-RPC error object (`{"error":{"code":..,"message":..}}`) for the request. The code is the JSON-RPC error code (e.g. -32601 method not found, -32000 server error, -32005 limit exceeded) and the message is the node's own description. This indicates the request reached the node but was rejected at the protocol/execution level.

Source

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

            .await
            .map_err(|e| anyhow::anyhow!("Failed to execute eth call RPC request: {e}"))?;
        let parsed =
            serde_json::from_slice::<RpcNodeHttpResponse<T>>(bytes.as_ref()).map_err(|e| {
                let raw_response = String::from_utf8_lossy(bytes.as_ref());
                let preview = rpc_response_preview(&raw_response);
                anyhow::anyhow!("Failed to parse eth call response: {e}\nRaw response: {preview}")
            })?;

        // Check for non-standard rate limit error (e.g., Infura)
        // These responses have code/message at top level without jsonrpc field
        if parsed.jsonrpc.is_none()
            && let (Some(code), Some(message)) = (parsed.code, parsed.message)
        {
            anyhow::bail!("RPC provider error {code}: {message}");
        }

        if let Some(error) = parsed.error {
            anyhow::bail!("RPC error {}: {}", error.code, error.message);
        }

        parsed
            .result
            .ok_or_else(|| anyhow::anyhow!("Response missing both result and error fields"))
    }

    /// Creates a properly formatted `eth_call` JSON-RPC request object targeting a specific contract address with encoded function data.
    #[must_use]
    pub fn construct_eth_call(
        &self,
        to: &str,
        call_data: &[u8],
        block: Option<u64>,
    ) -> serde_json::Value {
        self.construct_eth_call_request(None, to, call_data, block)
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Parse the numeric code: -32601 means unsupported method (check endpoint capabilities); -32602 means invalid params (fix arguments); execution errors mean fix the call itself
  2. Validate address/ABI parameters (checksummed 0x-hex, valid block tags) before sending
  3. Retry only for transient codes (e.g. -32005 limit exceeded) with backoff; fail fast on -32601/-32602
  4. Try a different RPC endpoint/node if the error is node-specific

Example fix

// before
let balance = rpc.get_balance_with_timeout(addr, None, timeout).await?;
// after: validate params first
let addr = address.to_checksum(None); // ensure valid 0x hex
if !addr.starts_with("0x") || addr.len() != 42 { anyhow::bail!("invalid address"); }
let balance = rpc.get_balance_with_timeout(addr, None, timeout).await?;
Defensive patterns

Strategy: try-catch

Validate before calling

fn is_valid_address(a: &str) -> bool { a.starts_with("0x") && a.len() == 42 && a[2..].chars().all(|c| c.is_ascii_hexdigit()) }

Try / catch

match rpc_call().await {
    Err(e) if e.to_string().contains("RPC error -32005") => retry_with_backoff(),
    Err(e) if e.to_string().contains("RPC error -32601") => switch_endpoint(),
    Err(e) => return Err(e),
    Ok(v) => Ok(v),
}

Prevention

When it happens

Trigger: Any `execute_rpc_call` / `get_balance_with_timeout` call where the node responds with a populated `error` field: unsupported method, invalid params, execution reverted, or node-side limit exceeded.

Common situations: Calling methods the node doesn't support (e.g. archive methods on a full node); malformed hex addresses or block tags in params; `eth_call` reverting due to contract logic; provider rejecting requests from unauthenticated or rate-limited projects.

Related errors


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