nautechsystems/nautilus_trader · error

Invalid address: {e}

Error message

Invalid address: {e}

What it means

`PoolIdentifier::new_checked` validates identifier strings, dispatching on a length-prefix (42 hex chars = EVM address, 66 = PoolId). For the 42-char case it parses the value as an EVM `Address`; if the hex string is not a valid address (bad hex, wrong length after validation gaps, parse failure), the error is wrapped as 'Invalid address: {e}'.

Source

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

    /// - String doesn't start with "0x"
    /// - Length is neither 42 nor 66 characters
    /// - Contains invalid hex characters
    /// - Address checksum validation fails (for Address variant)
    pub fn new_checked<T: AsRef<str>>(value: T) -> anyhow::Result<Self> {
        let value = value.as_ref();

        if !value.starts_with("0x") {
            anyhow::bail!("Pool identifier must start with '0x', was: {value}");
        }

        match value.len() {
            42 => {
                validate_hex_string(value)?;

                // Parse without strict checksum validation, then normalize to checksummed format
                let addr = value
                    .parse::<Address>()
                    .map_err(|e| anyhow::anyhow!("Invalid address: {e}"))?;

                // Store the checksummed version
                Ok(Self::Address(Ustr::from(addr.to_checksum(None).as_str())))
            }
            66 => {
                // PoolId variant (32 bytes)
                validate_hex_string(value)?;

                // Store lowercase version for consistency
                Ok(Self::PoolId(Ustr::from(&value.to_lowercase())))
            }
            len => {
                anyhow::bail!(
                    "Pool identifier must be 42 chars (address) or 66 chars (pool ID), was {len} chars: {value}"
                )
            }
        }
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Confirm the string is exactly `0x` + 40 hex characters before calling `new_checked`.
  2. Validate the address (e.g. regex `^0x[0-9a-fA-F]{40}$` or ethers/alloy parsing) upstream and show a clear user-facing message.
  3. Fix the data source emitting the identifier (truncation, missing 0x, wrong encoding).
  4. Use the parse error detail (`{e}`) to pinpoint which character/length failed and correct the input.

Example fix

// before
let id = PoolIdentifier::new_checked(user_input)?; // opaque error on typos
// after
fn is_evm_address(s: &str) -> bool {
    s.len() == 42 && s.starts_with("0x") && s[2..].chars().all(|c| c.is_ascii_hexdigit())
}
if !is_evm_address(user_input) {
    anyhow::bail!("'{}' is not a valid 0x + 40-hex EVM address", user_input);
}
let id = PoolIdentifier::new_checked(user_input)?;
Defensive patterns

Strategy: validation

Validate before calling

fn valid_evm_address(s: &str) -> bool {
    s.len() == 42 && s.starts_with("0x") && s[2..].chars().all(|c| c.is_ascii_hexdigit())
}

Try / catch

match PoolIdentifier::new_checked(value) {
    Ok(id) => id,
    Err(e) if e.to_string().starts_with("Invalid address:") => bail!("'{value}' is not a valid EVM address: {e}"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `new_checked` with a 42-character string that fails `Address` parsing — e.g. non-hex characters, '0x' prefix issues, or a value that passed `validate_hex_string` but isn't a canonical 20-byte address.

Common situations: Copy-pasting truncated or malformed contract addresses; addresses with mixed casing failing parse pre-checks; user-supplied pool/address config containing typos; ingesting identifiers from an external source with encoding drift.

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


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