nautechsystems/nautilus_trader · error

Failed to parse {type_name} to f64: {e}

Error message

Failed to parse {type_name} to f64: {e}

What it means

convert_to_f64 stringifies a U256/I256 amount and parses it back as f64, then scales by 10^decimals. The parse should never fail for decimal integer strings, so this error indicates an invariant violation — the Display output of the integer could not be parsed as f64 (e.g. an 'inf'/'NaN'-like string or a value that overflowed f64 parsing).

Source

Thrown at crates/adapters/blockchain/src/math.rs:52

    let amount = convert_to_f64(abs_amount, decimals, "I256")?;

    Ok(if is_negative { -amount } else { amount })
}

/// Convert an alloy's U256 value to f64, accounting for token decimals.
///
/// # Errors
///
/// Returns an error if the U256 value cannot be parsed to f64.
pub fn convert_u256_to_f64(amount: U256, decimals: u8) -> anyhow::Result<f64> {
    convert_to_f64(amount, decimals, "U256")
}

fn convert_to_f64(amount: impl Display, decimals: u8, type_name: &str) -> anyhow::Result<f64> {
    let amount: f64 = amount
        .to_string()
        .parse()
        .map_err(|e| anyhow::anyhow!("Failed to parse {type_name} to f64: {e}"))?;

    let factor = 10f64.powi(i32::from(decimals));
    Ok(amount / factor)
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use alloy::primitives::{I256, U256};
    use rstest::rstest;

    use super::*;

    #[rstest]
    fn test_convert_positive_i256_to_f64() {
        // Test with 6 decimals (USDC-like)
        let amount = I256::from_str("1000000").unwrap();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the actual string being parsed by logging amount.to_string(); it should be a plain decimal integer.
  2. Verify the input value is a legitimate U256/I256 from decoded event data, not a corrupted or wrapped value.
  3. If values can exceed f64 range with 0 decimals, keep amounts in U256/Decimal until final presentation instead of converting to f64.
  4. Consider replacing the string round-trip with TryFrom<f64>-free arithmetic to remove the parse failure mode.

Example fix

// before
let amount: f64 = amount.to_string().parse()
    .map_err(|e| anyhow::anyhow!("Failed to parse {type_name} to f64: {e}"))?;
// after (safe fallback to infinity with explicit signal)
let amount: f64 = amount.to_string().parse().unwrap_or_else(|_| {
    tracing::error!(type_name, "U256/I256 value not representable as f64");
    f64::INFINITY
});
Defensive patterns

Strategy: try-catch

Validate before calling

fn convertible_to_f64(amount: U256) -> bool {
    // f64 max ~1.8e308; U256 max ~1.15e77 fits, but guard the string round-trip anyway
    amount.to_string().parse::<f64>().is_ok()
}
// call convert_u256_to_f64 only after this check

Type guard

fn parses_as_f64(s: &str) -> bool {
    s.parse::<f64>().is_ok()
}

Try / catch

match convert_u256_to_f64(amount, decimals) {
    Ok(v) if v.is_finite() => use(v),
    Ok(_) => { /* infinity: value out of f64 range */ }
    Err(e) => tracing::error!(%e, "U256 to f64 conversion failed"),
}

Prevention

When it happens

Trigger: Calling convert_i256_to_f64 or convert_u256_to_f64 with an amount whose string form is not a valid f64 literal — practically only when a value renders as something non-numeric or exceeds f64's representable range (>= ~1.8e308), which is essentially unreachable via normal U256 usage.

Common situations: Converting a U256 near its maximum with 0 decimals is still ~1e77, well within f64 range, so hitting this in practice points to a bug, a wrapped/negative value mishandled upstream, or a custom Display impl.

Understand the failure class

Related errors


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