nautechsystems/nautilus_trader · error
Topic must be at least 32 bytes, was {}
Error message
Topic must be at least 32 bytes, was {} What it means
This error means the decoded topic bytes were shorter than 32 bytes when extracting an indexed address. Indexed Ethereum parameters occupy a full 32-byte topic, with addresses left-padded to occupy bytes 12..32. A shorter decode implies malformed hex (odd length, truncated string) or a corrupted topic value.
Source
Thrown at crates/adapters/blockchain/src/rpc/log.rs:147
decode_hex(hex)
}
/// Extract address from topic at index.
///
/// In Ethereum event logs, indexed address parameters are stored as 32-byte
/// values with the 20-byte address right-aligned (padded with zeros on the left).
///
/// # Errors
///
/// Returns an error if the topic is missing or the address extraction fails.
pub fn extract_address_from_topic(
log: &RpcLog,
index: usize,
description: &str,
) -> anyhow::Result<Address> {
let bytes = extract_topic_bytes(log, index)
.map_err(|_| anyhow::anyhow!("Missing {description} address in topic{index}"))?;
anyhow::ensure!(
bytes.len() >= 32,
"Topic must be at least 32 bytes, was {}",
bytes.len()
);
Ok(Address::from_slice(&bytes[12..32]))
}
/// Extract data bytes from RPC log.
///
/// # Errors
///
/// Returns an error if the hex decoding fails.
pub fn extract_data_bytes(log: &RpcLog) -> anyhow::Result<Vec<u8>> {
decode_hex(&log.data)
}
/// Validate event signature from topic0.
///View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the raw topic hex; it should be 66 characters (0x + 64 hex digits).
- Reject or quarantine logs with malformed topics instead of unwrapping.
- If your data source legitimately produces shorter encodings, left-pad to 32 bytes before extraction.
- Verify no intermediate transformation is trimming/hashing the topics.
Example fix
// before
let addr = extract_address_from_topic(&log, 1, "token0")?;
// after
let ok = log.topics.get(1).map_or(false, |t| t.trim_start_matches("0x").len() == 64);
anyhow::ensure!(ok, "malformed topic: {:?}", log.topics.get(1));
let addr = extract_address_from_topic(&log, 1, "token0")?; Defensive patterns
Strategy: validation
Validate before calling
fn topic_is_32_bytes(t: &str) -> bool {
t.trim_start_matches("0x").len() == 64
&& t.trim_start_matches("0x").chars().all(|c| c.is_ascii_hexdigit())
} Type guard
fn full_width_topic(log: &RpcLog, i: usize) -> bool {
log.topics.get(i).map_or(false, |t| t.trim_start_matches("0x").len() == 64)
} Try / catch
match extract_address_from_topic(&log, 1, "token0") {
Ok(a) => a,
Err(e) => { quarantine(&log, e); Address::ZERO }
} Prevention
- Topics should be 66 chars (0x + 64 hex digits) — reject shorter strings.
- Guard against odd-length hex which fails decoding.
- Watch for intermediaries truncating or re-encoding topics.
- Left-pad legitimately short encodings before extraction.
When it happens
Trigger: Calling extract_address_from_topic on a topic whose hex decodes to <32 bytes — e.g. a truncated topic string, odd-length hex, or a nonstandard producer emitting shortened topics.
Common situations: Corrupted or hand-crafted log fixtures, log data mangled by an intermediate service, or custom chains emitting nonstandard topic widths.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid contract address length: expected {} bytes, was {}
- Missing topic at index {index}
- Missing {description} address in topic{index}
- Invalid venue {}, expected Blockchain DEX format
- `router_addresses` must contain at least one router address
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/4383d9d3de0815fd.
Report an issue: GitHub.