nautechsystems/nautilus_trader · error
Pool ID hex must be 64 characters (32 bytes), was {}
Error message
Pool ID hex must be 64 characters (32 bytes), was {} What it means
`PoolIdentifier::from_pool_id_hex` builds a pool identifier from a hex string, which must be 64 hex characters (32 bytes) after an optional `0x` prefix. The library throws this when the stripped string length is not 64, since a 256-bit pool ID always encodes to exactly 64 hex chars.
Source
Thrown at crates/model/src/defi/pool_identifier.rs:141
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!(
hex_str.len() == 64,
"Pool ID hex must be 64 characters (32 bytes), was {}",
hex_str.len()
);
validate_hex_string(&format!("0x{hex_str}"))?;
Ok(Self::PoolId(Ustr::from(&format!(
"0x{}",
hex_str.to_lowercase()
))))
}
/// Returns the inner identifier value as a Ustr.
#[must_use]
pub fn inner(&self) -> Ustr {
match self {
Self::Address(s) | Self::PoolId(s) => *s,View on GitHub (pinned to 18893faf8b)
Solutions
- Trim the input and confirm `len == 64` (after stripping `0x`) before calling.
- If working with token/base addresses, use the Address constructor instead.
- Validate the string is pure hex with `validate_hex_string` or a regex like `^0x[0-9a-fA-F]{64}$`.
- Ensure config/env sources are not truncating or padding the value.
Example fix
// before
let id = PoolIdentifier::from_pool_id_hex(token_address_hex)?; // 40 chars
// after
assert_eq!(hex_str.trim_start_matches("0x").len(), 64);
let id = PoolIdentifier::from_pool_id_hex(pool_id_hex)?; Defensive patterns
Strategy: validation
Validate before calling
fn is_valid_pool_id_hex(s: &str) -> bool {
let h = s.strip_prefix("0x").unwrap_or(s);
h.len() == 64 && h.chars().all(|c| c.is_ascii_hexdigit())
} Type guard
fn is_pool_id_hex(s: &str) -> bool { is_valid_pool_id_hex(s) } Try / catch
match PoolIdentifier::from_pool_id_hex(input.trim()) {
Ok(id) => id,
Err(e) => { log::warn!("invalid pool id hex: {e}"); return Err(e); }
} Prevention
- Trim whitespace and normalize 0x prefix before validation
- Distinguish 40-char address hex from 64-char pool id hex in your data model
- Validate hex strings at config-load time, not at use time
When it happens
Trigger: Calling `from_pool_id_hex` with a 40-char address hex, a truncated hex string, extra characters (spaces, checksum chars beyond hex), or a non-hex-length string.
Common situations: Pasting an EVM address (40 hex chars) where a pool ID is expected, copying partial hex from logs, or handling strings with whitespace/newlines from config files.
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
- Pool ID must be 32 bytes, was {}
- Wrap amount must be positive
- Invalid tick range: {tick_lower} >= {tick_upper}
- Ticks {tick_lower} and {tick_upper} must be multiples of the
- Invalid tick bounds for {tick_lower} and {tick_upper}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/2e9fee6a3f3da28d.
Report an issue: GitHub.