{"record":{"id":"3d1b3ec30ff8cc27","repo":"nautechsystems/nautilus_trader","slug":"underflow-occurred-when-subtracting-quantity","errorCode":null,"errorMessage":"Underflow occurred when subtracting `Quantity`","messagePattern":"Underflow occurred when subtracting `Quantity`","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/types/quantity.rs","lineNumber":732,"sourceCode":"\nimpl<'a> Sum<&'a Self> for Quantity {\n    fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {\n        iter.copied().sum()\n    }\n}\n\nimpl Sub for Quantity {\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 `Quantity` values with mismatched decimal scales\"\n        );\n        Self {\n            raw: self\n                .raw\n                .checked_sub(rhs.raw)\n                .expect(\"Underflow occurred when subtracting `Quantity`\"),\n            precision: self.precision.max(rhs.precision),\n        }\n    }\n}\n\nimpl Mul for Quantity {\n    type Output = Self;\n    fn mul(self, rhs: Self) -> Self::Output {\n        let result_raw = if self.raw != QUANTITY_UNDEF\n            && rhs.raw != QUANTITY_UNDEF\n            && self.precision <= FIXED_PRECISION\n            && rhs.precision <= FIXED_PRECISION\n        {\n            checked_mul_div_fixed(self.raw, rhs.raw)\n        } else {\n            let scalar = QuantityRaw::try_from(raw_scale(self.precision.min(rhs.precision)))\n                .expect(\"Fixed-point scale fits QuantityRaw\");\n            checked_mul_div_raw(self.raw, rhs.raw, scalar)","sourceCodeStart":714,"sourceCodeEnd":750,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/types/quantity.rs#L714-L750","documentation":"Quantity implements Sub via checked_sub on raw fixed-point values; a None result (result negative or below the representable minimum) makes the expect panic. Since quantities are unsigned in normal builds, subtracting a larger Quantity from a smaller one is the primary cause.","triggerScenarios":"Using - on Quantity values with matching precision where lhs.raw < rhs.raw (would go negative), e.g. reducing a position by more than its size.","commonSituations":"Position/order quantity decrements without sufficient-size checks; double-subtraction from retries or duplicated fill events; fee/quantity bookkeeping bugs that over-deduct.","solutions":["Guard before subtracting: only subtract when self.raw >= rhs.raw (or self >= rhs via comparison), otherwise clamp to zero or reject.","Track available quantity and validate the decrement against it before applying (standard position-management check).","Compute deltas in Decimal (which supports negatives) when a signed remainder is meaningful.","Make fill/adjustment events idempotent (dedupe by trade ID) to avoid double subtraction."],"exampleFix":"// before\nlet remaining = filled - cancel_qty; // panics when cancel_qty > filled\n// after\nlet remaining = if filled >= cancel_qty { filled - cancel_qty } else { Quantity::zero(filled.precision) };","handlingStrategy":"validation","validationCode":"// Rust\nfn checked_quantity_sub(a: Quantity, b: Quantity) -> Option<Quantity> {\n    (a.precision == b.precision && a.raw >= b.raw)\n        .then(|| Quantity { raw: a.raw - b.raw, precision: a.precision })\n}","typeGuard":null,"tryCatchPattern":"// Check size before decrement:\nlet remaining = if position_qty >= reduce_qty {\n    position_qty - reduce_qty\n} else {\n    return Err(\"insufficient quantity to reduce\");\n};","preventionTips":["Always validate sufficient quantity before subtracting (position/order decrement checks).","Make fill and cancel handlers idempotent to avoid double subtraction.","Use Decimal for signed quantity deltas when a negative remainder is meaningful.","Add tests for over-reduction and duplicate-fill scenarios."],"tags":["rust","arithmetic-underflow","panic","quantity","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"}