{"record":{"id":"4f4369470bb658c7","repo":"nautechsystems/nautilus_trader","slug":"underflow-occurred-when-subtracting-price","errorCode":null,"errorMessage":"Underflow occurred when subtracting `Price`","messagePattern":"Underflow occurred when subtracting `Price`","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/types/price.rs","lineNumber":700,"sourceCode":"                .checked_add(rhs.raw)\n                .expect(\"Overflow occurred when adding `Price`\"),\n            precision: self.precision.max(rhs.precision),\n        }\n    }\n}\n\nimpl Sub for Price {\n    type Output = Self;\n    fn sub(self, rhs: Self) -> Self::Output {\n        assert!(\n            raw_scales_match(self.precision, rhs.precision),\n            \"Cannot subtract `Price` values with mismatched decimal scales\"\n        );\n        Self {\n            raw: self\n                .raw\n                .checked_sub(rhs.raw)\n                .expect(\"Underflow occurred when subtracting `Price`\"),\n            precision: self.precision.max(rhs.precision),\n        }\n    }\n}\n\nimpl Add<Decimal> for Price {\n    type Output = Decimal;\n    fn add(self, rhs: Decimal) -> Self::Output {\n        self.as_decimal() + rhs\n    }\n}\n\nimpl Sub<Decimal> for Price {\n    type Output = Decimal;\n    fn sub(self, rhs: Decimal) -> Self::Output {\n        self.as_decimal() - rhs\n    }\n}","sourceCodeStart":682,"sourceCodeEnd":718,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/types/price.rs#L682-L718","documentation":"Price implements Sub by checked_sub on raw fixed-point integers; if the result is below the representable range (most commonly a negative result, since Price is unsigned in normal builds), checked_sub returns None and this expect panics. Subtracting a larger Price from a smaller one is the typical cause.","triggerScenarios":"Using the - operator (impl Sub for Price) where lhs.raw < rhs.raw (negative difference) or the difference falls below PRICE_RAW_MIN, with matching precision required beforehand.","commonSituations":"Computing price deltas without knowing which side is larger; spread calculations where the spread can legitimately be negative; sorting/branching bugs that swap operands.","solutions":["Order operands: compute (max - min) or take .abs() semantics explicitly via Decimal before constructing a Price.","Compute differences in Decimal (price.as_decimal()) which supports negatives, and only build a Price from non-negative results.","Guard with a comparison (if a >= b { a - b } else { ... }) before using the - operator.","For signed deltas, use a dedicated signed type rather than Price."],"exampleFix":"// before\nlet spread = ask - bid; // panics when bid > ask (negative spread)\n// after\nlet spread = if ask >= bid { ask - bid } else { (bid - ask).neg_price() /* or track sign separately */ };","handlingStrategy":"validation","validationCode":"// Rust\nfn checked_price_sub(a: Price, b: Price) -> Option<Price> {\n    (a.precision == b.precision && a.raw >= b.raw).then(|| Price { raw: a.raw - b.raw, precision: a.precision })\n}","typeGuard":null,"tryCatchPattern":"// Compare before subtracting:\nlet spread = if ask >= bid { ask - bid } else { Decimal::ZERO }; // or track sign via Decimal","preventionTips":["Never subtract Prices without knowing which is larger; Price cannot represent negative values.","Use Decimal (as_decimal) for signed deltas and spreads.","Sort operands (max/min) when computing magnitude of differences.","Add assertions/tests on spread logic where bid > ask can occur with crossed books."],"tags":["rust","arithmetic-underflow","panic","price","subtraction"],"backgroundTag":"arithmetic-underflow","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"}