{"record":{"id":"8f7300d592aad4b0","repo":"nautechsystems/nautilus_trader","slug":"underflow-occurred-when-subtracting-money","errorCode":null,"errorMessage":"Underflow occurred when subtracting `Money`","messagePattern":"Underflow occurred when subtracting `Money`","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/types/money.rs","lineNumber":621,"sourceCode":"}\n\nimpl Sub for Money {\n    type Output = Self;\n    fn sub(self, rhs: Self) -> Self::Output {\n        assert_eq!(\n            self.currency, rhs.currency,\n            \"Currency mismatch: cannot subtract {} from {}\",\n            rhs.currency.code, self.currency.code\n        );\n        assert!(\n            raw_scales_match(self.currency.precision, rhs.currency.precision),\n            \"Cannot subtract `Money` values with mismatched decimal scales\"\n        );\n        Self {\n            raw: self\n                .raw\n                .checked_sub(rhs.raw)\n                .expect(\"Underflow occurred when subtracting `Money`\"),\n            currency: self.currency,\n        }\n    }\n}\n\nimpl Add<Decimal> for Money {\n    type Output = Decimal;\n    fn add(self, rhs: Decimal) -> Self::Output {\n        self.as_decimal() + rhs\n    }\n}\n\nimpl Sub<Decimal> for Money {\n    type Output = Decimal;\n    fn sub(self, rhs: Decimal) -> Self::Output {\n        self.as_decimal() - rhs\n    }\n}","sourceCodeStart":603,"sourceCodeEnd":639,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/types/money.rs#L603-L639","documentation":"The Sub impl for Money subtracts the raw fixed-point values with checked_sub and panics with this message on underflow (result below MoneyRaw minimum). Both operands must share currency precision. Overflow/underflow of the raw representation is treated as an unrecoverable invariant violation.","triggerScenarios":"money_a - money_b where the raw result is less than MONEY_RAW_MIN, e.g. subtracting a large positive balance from a near-minimum (deeply negative) amount.","commonSituations":"Computing net exposure or PnL where signed totals swing beyond the raw range;ledger reconciliation code subtracting accumulated balances; excessive negative accumulations from repeated fee deductions.","solutions":["Track the running value in a wider type (i128/Decimal) and clamp or validate before each subtraction.","Check against MONEY range bounds before subtracting.","Compile with high-precision so MoneyRaw is i128."],"exampleFix":"// before\nlet net = balance - withdrawal; // panics on underflow\n// after\nlet net_dec = balance.as_decimal() - withdrawal.as_decimal();\nlet net = Money::new(net_dec, USD); // range validated in constructor","handlingStrategy":"try-catch","validationCode":"if a.raw.checked_sub(b.raw).is_none() {\n    return Err(underflow_err());\n}\nlet diff = a - b;","typeGuard":"fn sub_will_underflow(a: Money, b: Money) -> bool {\n    a.raw.checked_sub(b.raw).is_none()\n}","tryCatchPattern":"// Money subtraction panics; pre-check before using the operator\nif a.raw.checked_sub(b.raw).is_none() {\n    // handle underflow (floor at min, alert, escalate)\n} else {\n    let diff = a - b;\n}","preventionTips":["Compute net positions in a wider signed type and clamp before converting back to Money.","Watch for repeated negative accumulations (fees, adjustments) drifting toward the minimum.","Add assertions on expected balance ranges in reconciliation code."],"tags":["rust","underflow","money","arithmetic"],"backgroundTag":"integer-overflow","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"}