nautechsystems/nautilus_trader · error

{method} RPC error {code}

Error message

{method} RPC error {code}

What it means

The typed execution-layer RPC call (`chain_id`, `get_storage_at`, `get_code_with_block`, `get_transaction_count_*`) received a non-standard error response containing a top-level `code` but no `jsonrpc` field. Like the untyped path, this indicates a provider-level error (commonly rate limiting on Infura-style endpoints) rather than a JSON-RPC protocol error. Only the code is included in the message here, not the provider message.

Source

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

        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!("{method} redirect rejected")
                }
                _ => anyhow::anyhow!("{method} request failed"),
            })?;

        let parsed = serde_json::from_slice::<RpcNodeHttpResponse<T>>(bytes.as_ref())
            .map_err(|_| anyhow::anyhow!("Failed to parse {method} response"))?;

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

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

        Ok(parsed.result)
    }

    /// Returns the chain ID reported by the RPC node via `eth_chainId`.
    ///
    /// # Errors
    ///
    /// Returns an error if the RPC call fails or the result is missing or malformed.
    pub async fn chain_id(&self) -> anyhow::Result<u64> {
        let result: Option<String> = self
            .execute_execution_rpc_call("eth_chainId", serde_json::json!([]))
            .await?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Back off and retry with exponential delay when the code indicates rate limiting (e.g. 429)
  2. Confirm the RPC URL and API key; test the endpoint with curl to see the raw provider response
  3. Reduce polling frequency or batch/subscribe (WebSocket/`eth_subscribe`) instead of repeated HTTP polls
  4. Route requests through a fallback provider when this error code is observed

Example fix

// before: tight polling loop
loop { let n = rpc.get_transaction_count_latest(addr).await?; tokio::time::sleep(Duration::from_millis(50)).await; }
// after: back off and fall back on provider errors
loop {
    match rpc.get_transaction_count_latest(addr).await {
        Ok(n) => break n,
        Err(e) if e.to_string().contains("RPC error") => { tokio::time::sleep(backoff.next()).await; continue; }
        Err(e) => return Err(e),
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// Pre-validate params and throttle request rate:
fn valid_hex(s: &str, len: usize) -> bool { s.starts_with("0x") && s.len() == len && s[2..].chars().all(|c| c.is_ascii_hexdigit()) }

Try / catch

match typed_call().await {
    Err(e) if e.to_string().contains("RPC error") => {
        tokio::time::sleep(backoff.next()).await; // provider-level error: back off, then fail over
        retry_or_fallback()
    }
    other => other,
}

Prevention

When it happens

Trigger: A call routed through `execute_execution_rpc_call` gets a response body with `jsonrpc: null` and top-level `code`/`message` — e.g. `eth_chainId` or `eth_getTransactionCount` hitting a rate-limited or misconfigured provider endpoint.

Common situations: Flooding a free-tier provider with transaction-count/storage polling loops; wrong provider endpoint returning gateway-level errors; expired project keys producing provider-throttle responses during startup or sync.

Related errors


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