nautechsystems/nautilus_trader · error

Failed to parse eth_getStorageAt response

Error message

Failed to parse eth_getStorageAt response

What it means

After a successful `eth_getStorageAt` call, the returned string is parsed into a `B256` (32-byte fixed hash). If the string is not valid hex of appropriate length, this error is raised (note: the original parse error is discarded, only a static message is returned).

Source

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

    #[allow(
        dead_code,
        reason = "Used by the independent verification read inventory"
    )]
    pub(crate) async fn get_storage_at(
        &self,
        address: &Address,
        slot: &B256,
        block: u64,
    ) -> anyhow::Result<B256> {
        let result: Option<String> = self
            .execute_execution_rpc_call(
                "eth_getStorageAt",
                serde_json::json!([address, slot, block_parameter(Some(block))]),
            )
            .await?;
        let value = result.ok_or_else(|| anyhow::anyhow!("eth_getStorageAt returned no result"))?;
        B256::from_str(&value)
            .map_err(|_| anyhow::anyhow!("Failed to parse eth_getStorageAt response"))
    }

    async fn get_code_with_block(
        &self,
        address: &Address,
        block: Option<u64>,
    ) -> anyhow::Result<Bytes> {
        let result: Option<String> = self
            .execute_execution_rpc_call(
                "eth_getCode",
                serde_json::json!([address, block_parameter(block)]),
            )
            .await?;
        let hex_string = result.ok_or_else(|| anyhow::anyhow!("eth_getCode returned no result"))?;
        let stripped = hex_string.strip_prefix("0x").unwrap_or(&hex_string);
        let bytes = hex::decode(stripped)
            .map_err(|e| anyhow::anyhow!("Failed to decode eth_getCode result: {e}"))?;
        Ok(Bytes::from(bytes))

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the node returns a full 32-byte 0x-prefixed hex string for eth_getStorageAt
  2. Use a compliant node (geth/erigon standard behavior) or update node version
  3. If values are unpadded, left-pad them to 64 hex chars before parsing
  4. Log the raw value to confirm its actual format

Example fix

// before: node returns unpadded value -> B256::from_str fails
let value = client.get_storage_at(&addr, slot, Some(block)).await?;
// after: left-pad to 32 bytes when necessary
let raw = format!("0x{:0>64}", raw_hex.trim_start_matches("0x"));
Defensive patterns

Strategy: validation

Validate before calling

fn is_b256_hex(s: &str) -> bool {
    let body = s.strip_prefix("0x").unwrap_or(s);
    body.len() == 64 && body.chars().all(|c| c.is_ascii_hexdigit())
}

Prevention

When it happens

Trigger: Node returns a storage value that is not a 32-byte hex string — e.g. a short hex value without padding (some chains return 0x + fewer bytes), decimal output, or a value with unexpected formatting.

Common situations: Non-standard or modified node implementations (some L2s or private chains) returning unpadded or differently-encoded storage values.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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