{"record":{"id":"9328cb9ce54494f7","repo":"nautechsystems/nautilus_trader","slug":"position-fill-void-exceeds-known-fragments-for","errorCode":null,"errorMessage":"position fill void exceeds known fragments for {}","messagePattern":"position fill void exceeds known fragments for (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/model/src/position.rs","lineNumber":748,"sourceCode":"    /// cycle boundaries its existing archive describes. `None` when the corrected history never\n    /// goes flat, so the current cycle covers all of it.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error when the allocation is stale, duplicated, or exceeds known fragments.\n    pub fn apply_fill_void(\n        &mut self,\n        event: OrderFillVoided,\n        voided_qty: Quantity,\n        commission_voided: Option<Money>,\n    ) -> anyhow::Result<Option<Money>> {\n        let fragment_qty = self\n            .fill_fragments(event.client_order_id, event.trade_id)\n            .iter()\n            .fold(Quantity::zero(self.size_precision), |total, fill| {\n                total + fill.last_qty\n            });\n        anyhow::ensure!(\n            !voided_qty.is_zero() && voided_qty <= fragment_qty,\n            \"position fill void exceeds known fragments for {}\",\n            event.trade_id,\n        );\n\n        if let Some(previous) = self.fill_voids.iter().rev().find(|record| {\n            record.event.client_order_id == event.client_order_id\n                && record.event.trade_id == event.trade_id\n        }) {\n            anyhow::ensure!(\n                voided_qty >= previous.voided_qty,\n                \"stale position fill void for {}\",\n                event.trade_id,\n            );\n            anyhow::ensure!(\n                voided_qty != previous.voided_qty\n                    || commission_voided != previous.commission_voided,\n                \"duplicate position fill void for {}\",","sourceCodeStart":730,"sourceCodeEnd":766,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/position.rs#L730-L766","documentation":"Position::apply_fill_void rejects a fill-void event whose voided quantity is zero or larger than the total quantity of known fill fragments recorded for that client_order_id/trade_id pair. The position model can only void fills it actually observed; voiding unknown or zero quantity would corrupt PnL and size accounting. This is an input-validation guard on position mutation.","triggerScenarios":"Calling apply_fill_void with a void event whose voided_qty is Quantity::zero, or with a trade_id/client_order_id combination whose accumulated fragment fills sum to less than the requested voided_qty (e.g. voiding a fill never recorded on this position, or after fragments were omitted due to a missed/replayed event stream).","commonSituations":"Reconciling an exchange's trade-cancel/adjustment messages against an incomplete local fill history; replaying historical data where a duplicate trade id caused fragments to be skipped; wiring a void event to the wrong position or instrument; passing quantity in the wrong units (contracts vs base) so the void exceeds recorded fragments.","solutions":["Verify the void event's client_order_id and trade_id match a fill previously applied to this position (check fill_fragments) before calling apply_fill_void.","Ensure voided_qty is a positive quantity (not zero) and expressed in the same units/precision as the original fill quantities.","Rebuild the position from the full event/replay history so all fill fragments are present before applying the void.","If the void legitimately exceeds locally known fragments, reconcile with the venue first (fetch authoritative fills) rather than forcing the void."],"exampleFix":"// before: voiding a fill that was never applied to this position\nposition.apply_fill_void(void_event)?; // panics/errors: exceeds known fragments\n// after: guard first\nlet known: Quantity = position\n    .fill_fragments(void_event.client_order_id, void_event.trade_id)\n    .iter()\n    .fold(Quantity::zero(position.size_precision), |t, f| t + f.last_qty);\nanyhow::ensure!(!void_event.voided_qty.is_zero() && void_event.voided_qty <= known,\n    \"skip void: known fragments {} < voided {}\", known, void_event.voided_qty);\nposition.apply_fill_void(void_event)?;","handlingStrategy":"validation","validationCode":"let known: Quantity = position\n    .fill_fragments(ev.client_order_id, ev.trade_id)\n    .iter()\n    .fold(Quantity::zero(position.size_precision), |t, f| t + f.last_qty);\nif ev.voided_qty.is_zero() || ev.voided_qty > known {\n    return Err(anyhow!(\"cannot void {}: known fragments {}\", ev.trade_id, known));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only emit void events for trades previously applied to the position.","Keep void quantities in the same units and precision as fill quantities.","Rebuild positions from complete event history before applying adjustments."],"tags":["position","pnl","validation","rust"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}