nautechsystems/nautilus_trader · error
Decimal overflow multiplying {lhs} by {rhs}
Error message
Decimal overflow multiplying {lhs} by {rhs} What it means
`checked_mul` wraps `Decimal::checked_mul` and raises this error when multiplying `lhs` by `rhs` overflows the Decimal range. Exposure and liability calculations multiply stakes by multipliers/prices, so two moderately large values can still overflow.
Source
Thrown at crates/model/src/data/bet.rs:776
if value.is_zero() {
anyhow::bail!("invalid {name}: must be non-zero")
}
Ok(())
}
fn checked_add(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {
lhs.checked_add(rhs)
.ok_or_else(|| anyhow::anyhow!("Decimal overflow adding {lhs} and {rhs}"))
}
fn checked_sub(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {
lhs.checked_sub(rhs)
.ok_or_else(|| anyhow::anyhow!("Decimal overflow subtracting {rhs} from {lhs}"))
}
fn checked_mul(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {
lhs.checked_mul(rhs)
.ok_or_else(|| anyhow::anyhow!("Decimal overflow multiplying {lhs} by {rhs}"))
}
fn checked_div(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {
check_nonzero_denominator(rhs, "divisor")?;
lhs.checked_div(rhs)
.ok_or_else(|| anyhow::anyhow!("Decimal overflow dividing {lhs} by {rhs}"))
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use super::*;
fn dec_str(s: &str) -> Decimal {
s.parse::<Decimal>().expect("Failed to parse Decimal")View on GitHub (pinned to 18893faf8b)
Solutions
- Check the operand values in the message; identify whether the stake or the multiplier is unreasonably large.
- Validate stake and odds ranges at input boundaries (e.g. reject stakes above a configured maximum).
- Fix unit scaling so amounts use consistent currency units before multiplication.
- Sanitize market feed multipliers/odds with upper-bound clamps before use in exposure math.
Example fix
// before
let exposure = checked_mul(stake, multiplier)?;
// after
let max_stake = Decimal::from(10_000_000);
if stake > max_stake { return Err(anyhow!("stake {stake} exceeds limit")); }
let exposure = checked_mul(stake, multiplier)?; Defensive patterns
Strategy: validation
Validate before calling
fn sane_stake(s: Decimal) -> bool { s > Decimal::ZERO && s < Decimal::from(10_000_000) }
fn sane_multiplier(m: Decimal) -> bool { m > Decimal::ONE && m < Decimal::from(10_000) } Try / catch
let exposure = bet.exposure_checked()
.map_err(|e| { log::error!("exposure overflow: {e}"); e })?; Prevention
- Enforce maximum stake and odds limits in your order/risk layer.
- Verify unit scaling of stakes and multipliers before arithmetic.
- Clamp feed multipliers to a sane upper bound.
When it happens
Trigger: Callers `exposure_checked`, `liability_checked`, `profit_checked`, or `hedging_stake_checked` multiplying a stake by a price/multiplier where the product exceeds Decimal's max (~7.9e28).
Common situations: Very large stake inputs (mis-scaled currency units, e.g. cents vs dollars compounded); extreme odds/multipliers from a corrupt feed; repeated compounding of stakes across bets.
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
- Decimal overflow adding {lhs} and {rhs}
- Decimal overflow subtracting {rhs} from {lhs}
- WETH balance overflow for included transaction {tx_hash} at
- invalid cumulative_quote_qty='{}' for cumulative_filled_qty=
- decimal `{value}` does not fit in i64
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/13abe07df263643b.
Report an issue: GitHub.