nautechsystems/nautilus_trader · error
Failed to decode pool ID hex: {e}
Error message
Failed to decode pool ID hex: {e} What it means
`PoolIdentifier::to_pool_id_bytes` decodes the PoolId variant's hex string into a fixed `[u8; 32]`. It errors when called on the `Address` variant, or when the stored hex cannot be decoded into exactly 32 bytes.
Source
Thrown at crates/model/src/defi/pool_identifier.rs:210
Self::Address(s) => Address::parse_checksummed(s.as_str(), None)
.map_err(|e| anyhow::anyhow!("Failed to parse address: {e}")),
Self::PoolId(_) => anyhow::bail!("Cannot convert PoolId variant to Address"),
}
}
/// Converts to native bytes array (V4 pools only).
///
/// Returns the 32-byte pool ID for use in V4-specific operations.
///
/// # Errors
///
/// Returns error if this is an Address variant or if hex decoding fails.
pub fn to_pool_id_bytes(&self) -> anyhow::Result<[u8; 32]> {
match self {
Self::PoolId(s) => {
let hex_str = s.strip_prefix("0x").unwrap_or(s.as_str());
hex::decode_array::<32>(hex_str)
.map_err(|e| anyhow::anyhow!("Failed to decode pool ID hex: {e}"))
}
Self::Address(_) => anyhow::bail!("Cannot convert Address variant to PoolId bytes"),
}
}
}
/// Validates that a string contains only valid hexadecimal characters after "0x" prefix.
fn validate_hex_string(s: &str) -> anyhow::Result<()> {
let hex_part = &s[2..];
if !hex_part.chars().all(|c| c.is_ascii_hexdigit()) {
anyhow::bail!("Invalid hex characters in: {s}");
}
Ok(())
}
impl PartialEq for PoolIdentifier {
fn eq(&self, other: &Self) -> bool {
match (self, other) {View on GitHub (pinned to 18893faf8b)
Solutions
- Match on the variant and only call `to_pool_id_bytes()` for `PoolIdentifier::PoolId`.
- For Address variants use `to_address()` and convert explicitly if a 32-byte form is truly needed.
- Re-derive the identifier via `from_pool_id_hex` to guarantee a valid 64-char hex before conversion.
Example fix
// before
let bytes = identifier.to_pool_id_bytes()?;
// after
let bytes = match &identifier {
PoolIdentifier::PoolId(_) => identifier.to_pool_id_bytes()?,
PoolIdentifier::Address(_) => bail!("expected PoolId variant"),
}; Defensive patterns
Strategy: type-guard
Validate before calling
fn expect_pool_id(id: &PoolIdentifier) -> Option<&Ustr> {
match id { PoolIdentifier::PoolId(p) => Some(p), _ => None }
} Type guard
fn is_pool_id_variant(id: &PoolIdentifier) -> bool {
matches!(id, PoolIdentifier::PoolId(_))
} Try / catch
match identifier.to_pool_id_bytes() {
Ok(bytes) => bytes,
Err(e) if e.to_string().contains("Address") => handle_address_variant(),
Err(e) => return Err(e),
} Prevention
- Match on the variant before converting
- Construct pool IDs only via from_pool_id_bytes/from_pool_id_hex so stored hex is guaranteed valid
- Keep addresses and pool IDs in separate collections/types rather than one mixed list
When it happens
Trigger: Calling `to_pool_id_bytes()` on a `PoolIdentifier::Address`, or on a PoolId whose hex is corrupt/wrong-length (should be prevented by construction, but possible if built through other paths).
Common situations: Code handling a heterogeneous collection of pool identifiers uniformly; hex values mutated or sourced from external feeds without validation.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse address: {e}
- Cannot convert PoolId variant to Address
- Cannot convert Address variant to PoolId bytes
- Router allowance {allowance} is below the swap amount {} for
- Input token {} balance {balance} is below the swap amount {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/fea54d86c0e2c267.
Report an issue: GitHub.