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
- Verify the source data is exactly 32 bytes before calling (e.g. `assert_eq!(bytes.len(), 32)` or decode with `hex::decode_array::<32>`).
- If you have a 20-byte address, use the `Address`-based constructor instead of `from_pool_id_bytes`.
- Check slicing offsets/indices on the parent buffer to ensure a full 32-byte word is captured.
- 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
- Never pass 20-byte addresses where 32-byte pool IDs are expected
- Decode hex with fixed-size helpers (hex::decode_array::<32>) so length is enforced by types
- Check slice offsets when extracting words from calldata or log topics
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
- Pool ID hex must be 64 characters (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/6e28e1150e1ec337.
Report an issue: GitHub.