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

extract_address_from_bytes expects an EVM log topic/data word: 32 bytes with the 20-byte address right-aligned in the last 20 bytes. It throws when the input slice is shorter than 32 bytes, since no full padded word is available. This guards against malformed log topics from the blockchain data source.

Source

Thrown at crates/adapters/blockchain/src/exchanges/parsing/core.rs:33

//! Shared core extraction functions for parsing event logs.
//!
//! These functions operate on raw bytes and are used by both HyperSync and RPC parsers
//! to ensure consistent extraction logic.

use alloy::primitives::Address;
use nautilus_core::hex;

/// Extract address from 32-byte topic (address in last 20 bytes).
///
/// 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 byte slice is shorter than 32 bytes.
pub fn extract_address_from_bytes(bytes: &[u8]) -> anyhow::Result<Address> {
    anyhow::ensure!(
        bytes.len() >= 32,
        "Topic must be at least 32 bytes, was {}",
        bytes.len()
    );
    Ok(Address::from_slice(&bytes[12..32]))
}

/// Extract u32 from 32-byte topic (value in last 4 bytes, big-endian).
///
/// In Ethereum event logs, indexed numeric parameters are stored as 32-byte
/// values with the number right-aligned in big-endian format.
///
/// # Errors
///
/// Returns an error if the byte slice is shorter than 32 bytes.
pub fn extract_u32_from_bytes(bytes: &[u8]) -> anyhow::Result<u32> {
    anyhow::ensure!(
        bytes.len() >= 32,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the log entry: ensure the topic field exists and is the full 32-byte padded word before calling
  2. Pass a 20-byte address left-padded to 32 bytes if you have a raw address
  3. Verify you are reading the correct topic index for the address field of this event
  4. Confirm the emitting contract actually uses the standard Solidity event layout (bytes32 topics)

Example fix

// before
let addr = extract_address_from_bytes(topic.as_ref())?; // topic may be None/short
// after
ensure!(topic.as_ref().map_or(false, |t| t.len() >= 32), "missing or truncated topic");
let addr = extract_address_from_bytes(topic.as_ref())?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_full_word(bytes: &[u8]) -> bool { bytes.len() >= 32 }

Type guard

fn as_topic_word(bytes: &[u8]) -> Option<&[u8; 32]> { bytes.get(..32)?.try_into().ok() }

Try / catch

match extract_address_from_bytes(topic) {
    Ok(addr) => use(addr),
    Err(e) if e.to_string().starts_with("Topic must be at least 32 bytes") => skip_malformed_log(),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling extract_address_from_bytes with a byte slice shorter than 32 bytes, e.g. a missing or truncated topic from a hypersync log, or passing a raw 20-byte address without 12 bytes of zero padding.

Common situations: A log event schema changed or the field index points at a topic that is absent/None; parsing a non-standard contract's event; passing data instead of topics (or vice versa).

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/24c04891945504d4. Report an issue: GitHub.