nautechsystems/nautilus_trader · error

Invalid currency1 topic length

Error message

Invalid currency1 topic length

What it means

Thrown by `parse_initialize_event_hypersync` when `topics[3]` exists but is not a full 32-byte word, so slicing bytes 12..32 (the last 20 bytes holding the Ethereum address) is impossible. Solidity indexed address topics are always 32 bytes; a shorter value indicates malformed log data from the indexer.

Source

Thrown at crates/adapters/blockchain/src/exchanges/parsing/uniswap_v4/initialize.rs:120

        .as_ref();
    let pool_identifier = Ustr::from(&hex::encode_prefixed(pool_id_bytes));

    let currency0 = Address::from_slice(
        topics[2]
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Missing currency0 topic"))?
            .as_ref()
            .get(12..32)
            .ok_or_else(|| anyhow::anyhow!("Invalid currency0 topic length"))?,
    );

    let currency1 = Address::from_slice(
        topics[3]
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Missing currency1 topic"))?
            .as_ref()
            .get(12..32)
            .ok_or_else(|| anyhow::anyhow!("Invalid currency1 topic length"))?,
    );

    if let Some(data) = log.data {
        let data_bytes = data.as_ref();

        // Validate minimum data length (5 fields × 32 bytes = 160 bytes)
        if data_bytes.len() < 160 {
            anyhow::bail!(
                "Initialize event data too short: expected at least 160 bytes, was {}",
                data_bytes.len()
            );
        }

        let decoded = <InitializeEventData as SolType>::abi_decode(data_bytes)
            .map_err(|e| anyhow::anyhow!("Failed to decode initialize event data: {e}"))?;

        let mut event = PoolCreatedEvent::new(
            block_number,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Validate `log.topics[3].as_ref().len() == 32` before parsing and skip/re-fetch malformed logs.
  2. Re-query HyperSync for the affected block/tx to rule out transient indexer truncation.
  3. Confirm you are using the Uniswap V4 Initialize event ABI (address-typed hooks topic) matching the deployed contract.
  4. Check the HyperSync client/schema version for topic encoding changes.

Example fix

// before
let currency1 = Address::from_slice(topics[3].as_ref()?.as_ref().get(12..32).ok_or_else(|| anyhow::anyhow!("Invalid currency1 topic length"))?);
// after
let topic3 = topics[3].as_ref().ok_or_else(|| anyhow::anyhow!("Missing currency1 topic"))?.as_ref();
anyhow::ensure!(topic3.len() == 32, "currency1 topic has {} bytes, expected 32", topic3.len());
let currency1 = Address::from_slice(&topic3[12..32]);
Defensive patterns

Strategy: validation

Validate before calling

fn topics_are_32_bytes(log: &HypersyncLog) -> bool {
    log.topics.iter().all(|t| t.as_ref().map(|v| v.as_ref().len() == 32).unwrap_or(false))
}

Type guard

fn valid_topic(t: &Option<HypersyncValue>) -> bool {
    t.as_ref().map(|v| v.as_ref().len() == 32).unwrap_or(false)
}

Try / catch

match parse_initialize_event_hypersync(&log) {
    Ok(ev) => store(ev),
    Err(e) if e.to_string().contains("topic length") => { re_fetch_log(&log).await?; }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `parse_initialize_event_hypersync` with a log whose `topics[3]` value contains fewer than 32 bytes. Occurs when HyperSync returns truncated/padded-differently topic values or a non-32-byte indexed parameter.

Common situations: Indexer data corruption or partial sync; querying logs from an event ABI where topic3 is not an address; mixing log formats from different HyperSync schema versions.

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/9fbdc6e8b87915b5. Report an issue: GitHub.