nautechsystems/nautilus_trader · error

Pool ID must be 32 bytes, was {}

Error message

Pool ID must be 32 bytes, was {}

What it means

`PoolIdentifier::from_pool_id_bytes` builds a DeFi pool identifier from raw 32-byte pool ID data. The library throws this error when the input slice is not exactly 32 bytes, because a pool ID is defined as a 256-bit value (encoded as 0x-prefixed 64-char hex). Any other length cannot represent a valid pool ID.

Source

Thrown at crates/model/src/defi/pool_identifier.rs:123

    pub fn new<T: AsRef<str>>(value: T) -> Self {
        Self::new_checked(value).expect(FAILED)
    }

    /// Creates an Address variant from an alloy Address.
    ///
    /// Returns the checksummed representation.
    #[must_use]
    pub fn from_address(address: Address) -> Self {
        Self::Address(Ustr::from(address.to_checksum(None).as_str()))
    }

    /// Creates a `PoolId` variant from raw bytes (32 bytes).
    ///
    /// # Errors
    ///
    /// Returns an error if bytes length is not 32.
    pub fn from_pool_id_bytes(bytes: &[u8]) -> anyhow::Result<Self> {
        anyhow::ensure!(
            bytes.len() == 32,
            "Pool ID must be 32 bytes, was {}",
            bytes.len()
        );

        Ok(Self::PoolId(Ustr::from(&hex::encode_prefixed(bytes))))
    }

    /// Creates a `PoolId` variant from a hex string (with or without 0x prefix).
    ///
    /// # Errors
    ///
    /// Returns an error if the string is not valid 64-character hex.
    pub fn from_pool_id_hex<T: AsRef<str>>(hex: T) -> anyhow::Result<Self> {
        let hex = hex.as_ref();
        let hex_str = hex.strip_prefix("0x").unwrap_or(hex);

        anyhow::ensure!(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the source data is exactly 32 bytes before calling (e.g. `assert_eq!(bytes.len(), 32)` or decode with `hex::decode_array::<32>`).
  2. If you have a 20-byte address, use the `Address`-based constructor instead of `from_pool_id_bytes`.
  3. Check slicing offsets/indices on the parent buffer to ensure a full 32-byte word is captured.
  4. Log the actual `bytes.len()` at the call site to diagnose truncation.

Example fix

// before
let id = PoolIdentifier::from_pool_id_bytes(&topic_bytes[12..])?; // 20 bytes
// after
let id = PoolIdentifier::from_pool_id_bytes(&topic_bytes)?; // full 32-byte topic
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_32_bytes(bytes: &[u8]) -> Result<(), String> {
    if bytes.len() == 32 { Ok(()) } else { Err(format!("expected 32 bytes, got {}", bytes.len())) }
}

Type guard

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

Try / catch

match PoolIdentifier::from_pool_id_bytes(&bytes) {
    Ok(id) => id,
    Err(e) => { log::warn!("bad pool id bytes: {e}"); PoolIdentifier::default() }
}

Prevention

When it happens

Trigger: Calling `from_pool_id_bytes` with a slice whose len != 32 — e.g. passing 20-byte Ethereum addresses, truncated hex output, or byte arrays decoded from shorter/longer hex strings.

Common situations: Developers confuse 20-byte EVM addresses with 32-byte pool IDs, slice a larger buffer (calldata, log topics) with wrong offsets, or decode hex without checking decoded length.

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/6e28e1150e1ec337. Report an issue: GitHub.