{"record":{"id":"1dafbdf5d764ce21","repo":"nautechsystems/nautilus_trader","slug":"decimal-overflow-subtracting-rhs-from-lhs","errorCode":null,"errorMessage":"Decimal overflow subtracting {rhs} from {lhs}","messagePattern":"Decimal overflow subtracting (.+?) from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/data/bet.rs","lineNumber":771,"sourceCode":"    }\n    Ok(())\n}\n\nfn check_nonzero_denominator(value: Decimal, name: &str) -> anyhow::Result<()> {\n    if value.is_zero() {\n        anyhow::bail!(\"invalid {name}: must be non-zero\")\n    }\n    Ok(())\n}\n\nfn 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;","sourceCodeStart":753,"sourceCodeEnd":789,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/data/bet.rs#L753-L789","documentation":"`checked_sub` wraps `Decimal::checked_sub` and raises this error when subtracting `rhs` from `lhs` would overflow the Decimal range (typically lhs very negative or rhs very large in magnitude). The error message includes both operands for diagnosis.","triggerScenarios":"Callers `from_liability_checked`, `liability_checked`, `profit_checked`, or `inverse_probability_to_bet` subtracting liabilities/probability-derived values whose difference exceeds Decimal's representable range.","commonSituations":"Computing profit with an extreme liability value from a bad feed; an invalid probability (e.g. far outside 0..1) fed into `inverse_probability_to_bet` producing enormous reciprocals; negative liabilities of huge magnitude from corrupted data.","solutions":["Validate inputs upstream: ensure probabilities are within 0..1 and liabilities are within sane bounds before the subtraction.","Log/inspect `lhs` and `rhs` from the error message to identify the bad operand.","Sanitize feed data (clamp liabilities, reject NaN/absurd values) at ingestion time.","If genuine large magnitudes are required, use a wider numeric representation for these calculations."],"exampleFix":"// before\nlet profit = checked_sub(proceeds, liability)?;\n\n// after\nassert!(liability.abs() < Decimal::from(1_000_000_000), \"liability out of sane range\");\nlet profit = checked_sub(proceeds, liability)?;","handlingStrategy":"validation","validationCode":"fn valid_probability(p: Decimal) -> bool { p > Decimal::ZERO && p < Decimal::ONE }\nfn sane_liability(v: Decimal) -> bool { v.abs() < Decimal::from(1_000_000_000) }","typeGuard":null,"tryCatchPattern":"let profit = profit_checked(&bet)\n    .map_err(|e| { log::error!(\"profit calc overflow: {e}\"); e })?;","preventionTips":["Validate probabilities are strictly within 0..1 before inverse computations.","Sanitize feed-provided liabilities with upper-bound clamps.","Reject NaN-like or absurd values at market-data ingestion."],"tags":["rust","decimal","overflow","arithmetic"],"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"}