nautechsystems/nautilus_trader · error

effective raw scale should fit in MoneyRaw

Error message

effective raw scale should fit in MoneyRaw

What it means

Money's Display impl (fmt) divides the raw value by the effective raw scale to render the amount. The scale 10^precision is converted into MoneyRaw via try_from, and if the currency precision implies a scale not representable in MoneyRaw the conversion expect panics with this message. Displaying can therefore panic on pathological precision values.

Source

Thrown at crates/model/src/types/money.rs:690

        self.as_f64() * rhs
    }
}

impl Div<f64> for Money {
    type Output = f64;
    fn div(self, rhs: f64) -> Self::Output {
        self.as_f64() / rhs
    }
}

impl Debug for Money {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.currency.precision > crate::types::fixed::MAX_FLOAT_PRECISION {
            write!(f, "{}({}, {})", stringify!(Money), self.raw, self.currency)
        } else {
            let precision = self.currency.precision;
            let scale = MoneyRaw::try_from(raw_scale(precision))
                .expect("effective raw scale should fit in MoneyRaw");
            let currency_scale = MoneyRaw::pow(10, u32::from(precision));
            let amount = self.raw / (scale / currency_scale);

            if precision == 0 {
                write!(f, "{}({}, {})", stringify!(Money), amount, self.currency)
            } else {
                let sign = if amount < 0 { "-" } else { "" };
                let amount_abs = amount.unsigned_abs();
                let currency_scale = currency_scale.unsigned_abs();
                let whole = amount_abs / currency_scale;
                let fraction = amount_abs % currency_scale;
                write!(
                    f,
                    "{}({sign}{whole}.{fraction:0>width$}, {})",
                    stringify!(Money),
                    self.currency,
                    width = usize::from(precision),
                )

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Correct the Currency's precision to a realistic value (typically 0-9 decimal places).
  2. Validate currency precision when constructing/registering the currency.
  3. Avoid formatting until the currency definition is fixed; inspect the raw value instead.

Example fix

// before
let cur = Currency::from("XYZ").with_precision(45);
println!("{}", Money::new(dec, cur)); // panics in fmt
// after
let cur = Currency::from("XYZ").with_precision(8); // sane precision
println!("{}", Money::new(dec, cur));
Defensive patterns

Strategy: validation

Validate before calling

if currency.precision > 38 { // 10^38 overflows i128 MoneyRaw
    panic!("currency precision {} too large for display", currency.precision);
}
format!("{}", money)

Type guard

fn printable(currency: &Currency) -> bool {
    u32::from(currency.precision) <= 38 && u64::from(currency.precision) <= u32::MAX.into()
}

Prevention

When it happens

Trigger: Formatting (println!, format!, to_string, logging) any Money whose currency precision exceeds what MoneyRaw can represent as 10^precision (roughly precision > 38 for i128 raw, lower in non-high-precision builds).

Common situations: A Currency configured with an invalid/extreme precision (bad asset definition from a config or exchange adapter); debugging output of a mis-constructed Money. Note the code prints raw fallback only when precision > MAX_FLOAT_PRECISION, so extreme-but-below-that precisions still hit the scale conversion.

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/e4b028427b54e8eb. Report an issue: GitHub.