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
- Verify the stored hash value is exactly 64 hex characters (with or without a consistent 0x prefix) and re-encode it correctly
- Fix the data source / migration that wrote the malformed hash
- Strip a leading 0x and validate with hex::decode before calling the parser
- 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
- Store hashes as fixed-width 32-byte values or canonical 0x + 64-hex strings
- Enforce column widths >= 66 chars for hex hash columns
- Validate hash format at ingestion, not at parse time
- Do not mix address (20-byte) and hash (32-byte) hex values in the same field
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
- Invalid hex u64: {e}
- Invalid hex u32: {e}
- Failed to parse hex quantity '{s}': {e}
- Invalid receipt log data
- Invalid receipt log topic
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/42121160ef6e3bf9.
Report an issue: GitHub.