nautechsystems/nautilus_trader · error · anyhow::Error

Cannot convert PoolId variant to Address

Error message

Cannot convert PoolId variant to Address

What it means

PoolIdentifier is an enum of Address (20-byte) and PoolId (32-byte V4) variants; to_address only makes sense for the Address variant. Calling it on a PoolId variant bails with this error because a 32-byte pool ID cannot be represented as a 20-byte Address.

Source

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

    /// Returns true if this is a `PoolId` variant (V4 pools).
    #[must_use]
    pub fn is_pool_id(&self) -> bool {
        matches!(self, Self::PoolId(_))
    }

    /// Converts to native Address type (V2/V3 pools only).
    ///
    /// Returns the underlying Address for use with alloy/ethers operations.
    ///
    /// # Errors
    ///
    /// Returns error if this is a `PoolId` variant or if parsing fails.
    pub fn to_address(&self) -> anyhow::Result<Address> {
        match self {
            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"),

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Match on the PoolIdentifier enum and handle the PoolId variant separately before calling to_address
  2. Use to_pool_id_bytes for V4 pool identifiers instead
  3. Check the variant with matches! or an is-style helper before conversion

Example fix

// before
let addr = pool_id.to_address()?;
// after
let addr = match &pool_id {
    PoolIdentifier::Address(_) => pool_id.to_address()?,
    PoolIdentifier::PoolId(_) => anyhow::bail!("V4 pool: use to_pool_id_bytes()"),
};
Defensive patterns

Strategy: type-guard

Validate before calling

matches!(pid, PoolIdentifier::Address(_))

Type guard

fn is_address_variant(pid: &PoolIdentifier) -> bool {
    matches!(pid, PoolIdentifier::Address(_))
}

Try / catch

match pid.to_address() {
    Ok(a) => use_address(a),
    Err(e) if e.to_string().contains("PoolId variant") => handle_v4_pool(pid),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling pool_identifier.to_address() on a value constructed via new_checked with a 66-char pool ID or from_pool_id_hex.

Common situations: Generic code that assumes all pool identifiers are addresses; subscribing to Uniswap V4 pools (which use pool IDs) and then calling address-only APIs.

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/5f3b8e6086d7ba54. Report an issue: GitHub.