nautechsystems/nautilus_trader · error

Decimal exponent {exponent} exceeds u8 range

Error message

Decimal exponent {exponent} exceeds u8 range

What it means

`SwapTradeInfo::execution_price` computes a raw U256 price as `quote_amount * 10^(base_decimals + FIXED_PRECISION - quote_decimals) / base_amount`. When token decimals make that exponent exceed what fits in a `u8` (i.e. > 255 or negative handling), the `u8::try_from` conversion fails and this error is thrown. It guards against absurd decimal configurations that cannot be represented in the fixed-point scaling used.

Source

Thrown at crates/model/src/defi/data/swap_trade_info.rs:418

        }

        // Determine base and quote amounts/decimals based on inversion
        let (quote_amount, base_amount, quote_decimals, base_decimals) = if self.is_inverted {
            // inverted: token0=quote, token1=base
            (amount0, amount1, self.token0.decimals, self.token1.decimals)
        } else {
            // not inverted: token0=base, token1=quote
            (amount1, amount0, self.token1.decimals, self.token0.decimals)
        };

        FullMath::check_decimal_exponent(base_decimals)?;
        FullMath::check_decimal_exponent(quote_decimals)?;

        let exponent =
            i16::from(base_decimals) + i16::from(FIXED_PRECISION) - i16::from(quote_decimals);
        let price_raw_u256 = if exponent >= 0 {
            let exponent = u8::try_from(exponent)
                .map_err(|_| anyhow::anyhow!("Decimal exponent {exponent} exceeds u8 range"))?;
            let primary_exponent = exponent.min(DECIMAL_EXPONENT_MAX);
            let secondary_exponent = exponent - primary_exponent;
            let primary_scalar = FullMath::pow10(primary_exponent)?;
            let secondary_scalar = FullMath::pow10(secondary_exponent)?;
            FullMath::mul_div_scaled(
                quote_amount,
                U256::from(1),
                base_amount,
                &[primary_scalar, secondary_scalar],
            )?
        } else {
            let divisor_exponent = u8::try_from(exponent.unsigned_abs())
                .map_err(|_| anyhow::anyhow!("Decimal exponent {exponent} exceeds u8 range"))?;
            let divisor = FullMath::pow10(divisor_exponent)?;
            (quote_amount / base_amount) / divisor
        };

        price_from_u256(price_raw_u256)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check token decimal metadata (base_decimals, quote_decimals) for correctness at data-ingestion time; reject values > ~38 (realistic ERC-20 range).
  2. Swap base/quote assignment if tokens were mislabeled.
  3. Sanitize/validate `FIXED_PRECISION` usage with your token pair before calling; skip the pair if exponent is out of range.
  4. Handle the anyhow error at the call site and skip the event rather than aborting replay.

Example fix

// before
let price = trade_info.execution_price(base_decimals, quote_decimals)?; // panics pipeline on bad decimals
// after
if base_decimals > 36 || quote_decimals > 36 {
    return Ok(None); // skip implausible token metadata
}
let price = trade_info.execution_price(base_decimals, quote_decimals).ok();
Defensive patterns

Strategy: validation

Validate before calling

fn exponent_ok(base_decimals: u8, quote_decimals: u8, fixed_precision: u32) -> bool {
    let e = i64::from(base_decimals) + i64::from(fixed_precision) - i64::from(quote_decimals);
    (0..=255).contains(&e)
}

Try / catch

match quote.calculate_trade_info(&token0, &token1) {
    Ok(()) => { /* use trade info */ }
    Err(e) if e.to_string().contains("exceeds u8 range") => eprintln!("skipping pair: bad decimals: {e}"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `execution_price` (directly or via `SwapTradeInfo` construction / `calculate_trade_info`) with token decimals such that `base_decimals + FIXED_PRECISION - quote_decimals > 255` (positive branch).

Common situations: Malformed or adversarial token metadata from on-chain data where `base_decimals` is huge or misparsed; mixing up which token is base vs quote so decimals arithmetic explodes.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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