nautechsystems/nautilus_trader · error · anyhow::Error

Pool identifier must be 42 chars (address) or 66 chars (pool

Error message

Pool identifier must be 42 chars (address) or 66 chars (pool ID), was {len} chars: {value}

What it means

PoolIdentifier::new_checked only accepts identifiers of exactly 42 characters (an EVM address) or 66 characters (a 32-byte V4 pool ID), both with 0x prefix and valid hex. Any other length is rejected. This enforces the two canonical pool identifier shapes the model layer supports.

Source

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

                validate_hex_string(value)?;

                // Parse without strict checksum validation, then normalize to checksummed format
                let addr = value
                    .parse::<Address>()
                    .map_err(|e| anyhow::anyhow!("Invalid address: {e}"))?;

                // Store the checksummed version
                Ok(Self::Address(Ustr::from(addr.to_checksum(None).as_str())))
            }
            66 => {
                // PoolId variant (32 bytes)
                validate_hex_string(value)?;

                // Store lowercase version for consistency
                Ok(Self::PoolId(Ustr::from(&value.to_lowercase())))
            }
            len => {
                anyhow::bail!(
                    "Pool identifier must be 42 chars (address) or 66 chars (pool ID), was {len} chars: {value}"
                )
            }
        }
    }

    /// Creates a new [`PoolIdentifier`] instance.
    ///
    /// # Panics
    ///
    /// Panics if validation fails.
    #[must_use]
    pub fn new<T: AsRef<str>>(value: T) -> Self {
        Self::new_checked(value).expect(FAILED)
    }

    /// Creates an Address variant from an alloy Address.
    ///

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Count the characters of the value and ensure it is exactly 42 or 66 including the '0x' prefix
  2. Add the '0x' prefix if missing (an unprefixed 40-char address or 64-char pool ID fails)
  3. Verify you are passing an on-chain identifier, not a human-readable pool name or symbol
  4. Use PoolIdentifier::new_address or from_pool_id_hex helpers which make the intended variant explicit

Example fix

// before
let pid = PoolIdentifier::new_checked("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")?;
// after
let pid = PoolIdentifier::new_checked("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")?; // 42 chars
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_pool_identifier(s: &str) -> bool {
    let len = s.len();
    (len == 42 || len == 66) && s.starts_with("0x") && s[2..].chars().all(|c| c.is_ascii_hexdigit())
}

Prevention

When it happens

Trigger: Calling PoolIdentifier::new_checked with a string whose length is not 42 or 66 — e.g. a truncated hex string, an address missing '0x' (40 chars), a pool ID without '0x' (64 chars), or arbitrary pool names/symbols.

Common situations: Config files or venue parameters holding a shortened or unprefixed address; passing a Uniswap V3 fee-tier name or pool label instead of an on-chain identifier; slicing errors when copying pool IDs from explorers.

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/5995a6441e051124. Report an issue: GitHub.