nautechsystems/nautilus_trader · error

Invalid receipt log hash

Error message

Invalid receipt log hash

What it means

parse_optional_hash converts an optional string field of a transaction receipt log into a B256 (32-byte) hash. If the string is present but not a valid 32-byte hex hash (B256::from_str fails), the error 'Invalid receipt log hash' is returned. This protects downstream code from using malformed log identifiers (e.g. block hash, transaction hash, or log-related hash fields) read from stored or provided receipt data.

Source

Thrown at crates/adapters/blockchain/src/rpc/verification.rs:1945

}

impl NormalizedValue for VerifiedReceipt {
    fn write_normalized(&self, output: &mut Vec<u8>) {
        output.push(22);
        self.transaction_hash.write_normalized(output);
        self.block_hash.write_normalized(output);
        self.block_number.write_normalized(output);
        self.gas_used.write_normalized(output);
        self.effective_gas_price.write_normalized(output);
        self.transaction_index.write_normalized(output);
        self.status.write_normalized(output);
        self.logs.write_normalized(output);
    }
}

fn parse_optional_hash(value: Option<&str>) -> anyhow::Result<Option<B256>> {
    value
        .map(|value| B256::from_str(value).map_err(|_| anyhow::anyhow!("Invalid receipt log hash")))
        .transpose()
}

fn parse_optional_quantity(value: Option<&str>) -> anyhow::Result<Option<u64>> {
    value
        .map(|value| {
            let value = value.strip_prefix("0x").unwrap_or(value);
            u64::from_str_radix(value, 16)
                .map_err(|_| anyhow::anyhow!("Invalid receipt log quantity"))
        })
        .transpose()
}

#[cfg(test)]
mod tests {
    use alloy::{
        primitives::{
            U256, address,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the stored hash value is exactly 64 hex characters (with or without a consistent 0x prefix) and re-encode it correctly
  2. Fix the data source / migration that wrote the malformed hash
  3. Strip a leading 0x and validate with hex::decode before calling the parser
  4. Skip or flag logs with invalid hashes instead of failing the whole receipt parse, if the field is truly optional

Example fix

// before
parse_optional_hash(Some("0x1234"))?; // too short -> Invalid receipt log hash
// after
let h = "0x1234";
let clean = h.trim_start_matches("0x");
if clean.len() == 64 && clean.chars().all(|c| c.is_ascii_hexdigit()) {
    parse_optional_hash(h)?;
}
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_b256_hex(s: &str) -> bool {
    let h = s.trim_start_matches("0x");
    h.len() == 64 && h.chars().all(|c| c.is_ascii_hexdigit())
}

Type guard

fn as_b256(value: Option<&str>) -> Option<B256> {
    value.and_then(|v| B256::from_str(v).ok())
}

Try / catch

match parse_optional_hash(log_row.hash.as_deref()) {
    Ok(hash) => use_hash(hash),
    Err(e) => { warn!(row_id = %log_row.id, "skipping log with {e}"); continue; }
}

Prevention

When it happens

Trigger: Passing a receipt-log row/string whose hash field contains a non-hex string, a hex string shorter or longer than 64 hex chars, or a value with an unexpected 0x-prefix format into the receipt-log parsing path (verification.rs:~1945).

Common situations: Corrupted or hand-edited database rows for receipt logs; hashes truncated by column width; values stored without 0x handling that from_str rejects; mixing 32-byte hashes with 20-byte address hex strings.

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/42121160ef6e3bf9. Report an issue: GitHub.