nautechsystems/nautilus_trader · error · anyhow::Error
Cannot convert Address variant to PoolId bytes
Error message
Cannot convert Address variant to PoolId bytes
What it means
Variant guard in PoolIdentifier::to_pool_id_bytes: the 32-byte pool ID only exists for the PoolId variant (V4 pools); calling it on an Address-variant identifier (V2/V3 pools) has no meaningful result and bails.
Source
Thrown at crates/model/src/defi/pool_identifier.rs:212
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) {
(Self::Address(a), Self::Address(b)) | (Self::PoolId(a), Self::PoolId(b)) => {
// Case-insensitive comparisonView on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the identifier comes from a V4 pool with a 66-char pool ID before calling to_pool_id_bytes
- Use to_address for V2/V3 pool identifiers
- Match on the enum variant and branch the conversion accordingly
Example fix
// before
let bytes = v3_pool.to_pool_id_bytes()?;
// after
if let PoolIdentifier::Address(addr) = &v3_pool {
let bytes = addr.to_address()?;
} Defensive patterns
Strategy: type-guard
Validate before calling
matches!(pid, PoolIdentifier::PoolId(_))
Type guard
fn is_pool_id_variant(pid: &PoolIdentifier) -> bool {
matches!(pid, PoolIdentifier::PoolId(_))
} Try / catch
match pid.to_pool_id_bytes() {
Ok(bytes) => use_pool_id(bytes),
Err(e) if e.to_string().contains("Address variant") => handle_v2_v3_pool(pid),
Err(e) => return Err(e),
} Prevention
- Only use to_pool_id_bytes on identifiers built from 66-char pool IDs
- Keep V4 and V2/V3 pool handling in separate code paths
When it happens
Trigger: Calling pool_identifier.to_pool_id_bytes() on a value built from a 42-char EVM address (e.g. a Uniswap V2/V3 pool address).
Common situations: Passing a V2/V3 pool address into a code path expecting a V4 pool ID; mixing pool types in subscription configuration.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Cannot convert PoolId variant to Address
- Expected Custom data variant
- Actor type mismatch for '{id}': expected {expected_type:?},
- Invalid NodeState value
- Native currency not specified for chain {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/9c957da8938d0b80.
Report an issue: GitHub.