{"record":{"id":"4d4add3cae3d1dd9","repo":"nautechsystems/nautilus_trader","slug":"decimal-overflow-dividing-lhs-by-rhs","errorCode":null,"errorMessage":"Decimal overflow dividing {lhs} by {rhs}","messagePattern":"Decimal overflow dividing (.+?) by (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/data/bet.rs","lineNumber":782,"sourceCode":"fn checked_add(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {\n    lhs.checked_add(rhs)\n        .ok_or_else(|| anyhow::anyhow!(\"Decimal overflow adding {lhs} and {rhs}\"))\n}\n\nfn checked_sub(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {\n    lhs.checked_sub(rhs)\n        .ok_or_else(|| anyhow::anyhow!(\"Decimal overflow subtracting {rhs} from {lhs}\"))\n}\n\nfn checked_mul(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {\n    lhs.checked_mul(rhs)\n        .ok_or_else(|| anyhow::anyhow!(\"Decimal overflow multiplying {lhs} by {rhs}\"))\n}\n\nfn checked_div(lhs: Decimal, rhs: Decimal) -> anyhow::Result<Decimal> {\n    check_nonzero_denominator(rhs, \"divisor\")?;\n    lhs.checked_div(rhs)\n        .ok_or_else(|| anyhow::anyhow!(\"Decimal overflow dividing {lhs} by {rhs}\"))\n}\n\n#[cfg(test)]\nmod tests {\n    use rstest::rstest;\n    use rust_decimal::Decimal;\n    use rust_decimal_macros::dec;\n\n    use super::*;\n\n    fn dec_str(s: &str) -> Decimal {\n        s.parse::<Decimal>().expect(\"Failed to parse Decimal\")\n    }\n\n    #[rstest]\n    #[should_panic(expected = \"Liability-based betting is only applicable for Lay side.\")]\n    fn test_from_liability_panics_on_back_side() {\n        let _ = Bet::from_liability(dec!(2.0), dec!(100.0), BetSide::Back);","sourceCodeStart":764,"sourceCodeEnd":800,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/data/bet.rs#L764-L800","documentation":"`checked_div` first verifies the denominator is non-zero (via `check_nonzero_denominator`), then wraps `Decimal::checked_div`, raising this error if the division itself overflows the Decimal range (e.g. dividing by an extremely small non-zero denominator).","triggerScenarios":"Callers such as `from_liability_checked`, `hedging_stake_checked`, `as_bet_checked`, `increased_state`, `decreased_state`, or `flattening_bet_checked` dividing by a near-zero denominator (e.g. a probability of ~1e-28) so the quotient overflows.","commonSituations":"Converting liability to back/stake amounts with a near-zero probability from a bad odds feed; a zero-ish scale factor slipping past the non-zero check; extreme hedging ratios computed from corrupt market data.","solutions":["Validate the denominator against a minimum sane threshold (not just non-zero) before dividing.","Inspect the operands in the message to find which value is a near-zero denominator or oversized numerator.","Sanitize probabilities/odds at ingestion (clamp to e.g. [1e-6, 1.0]) so reciprocals stay in range.","If a genuine extreme quotient is expected, use a wider numeric type for that calculation."],"exampleFix":"// before\nlet stake = checked_div(liability, probability)?;\n\n// after\nlet min_prob = Decimal::new(1, 6); // 0.000001\nif probability < min_prob { return Err(anyhow!(\"probability {probability} too small\")); }\nlet stake = checked_div(liability, probability)?;","handlingStrategy":"validation","validationCode":"let min_denom = Decimal::new(1, 6); // 0.000001\nif denominator.abs() < min_denom {\n    return Err(anyhow!(\"denominator {denominator} too close to zero\"));\n}","typeGuard":null,"tryCatchPattern":"let stake = hedging_stake_checked(&bet)\n    .map_err(|e| { log::error!(\"hedge calc overflow: {e}\"); e })?;","preventionTips":["Enforce a minimum-threshold check on denominators, not just non-zero.","Clamp probabilities/odds to e.g. [1e-6, 1.0] at ingestion.","Sanitize market data before any reciprocal or ratio computation."],"tags":["rust","decimal","overflow","division"],"backgroundTag":"value-out-of-range","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}