{"record":{"id":"e00c3ecd89376322","repo":"nautechsystems/nautilus_trader","slug":"venue-order-id-differs-across-fill-group","errorCode":null,"errorMessage":"venue order ID differs across fill group","messagePattern":"venue order ID differs across fill group","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/live/src/execution/manager.rs","lineNumber":1283,"sourceCode":"        instrument: &InstrumentAny,\n    ) -> anyhow::Result<OrderStatusReport> {\n        let Some(first) = fills.first() else {\n            anyhow::bail!(\"fill group is empty\");\n        };\n        let venue_position_id = first\n            .venue_position_id\n            .ok_or_else(|| anyhow::anyhow!(\"venue position ID is missing\"))?;\n\n        for fill in fills.iter().skip(1) {\n            anyhow::ensure!(\n                fill.account_id == first.account_id,\n                \"account ID differs across fill group\"\n            );\n            anyhow::ensure!(\n                fill.instrument_id == first.instrument_id,\n                \"instrument ID differs across fill group\"\n            );\n            anyhow::ensure!(\n                fill.venue_order_id == first.venue_order_id,\n                \"venue order ID differs across fill group\"\n            );\n            anyhow::ensure!(\n                fill.client_order_id == first.client_order_id,\n                \"client order ID differs across fill group\"\n            );\n            anyhow::ensure!(\n                fill.order_side == first.order_side,\n                \"order side differs across fill group\"\n            );\n            anyhow::ensure!(\n                fill.venue_position_id == first.venue_position_id,\n                \"venue position ID differs across fill group\"\n            );\n        }\n\n        anyhow::ensure!(","sourceCodeStart":1265,"sourceCodeEnd":1301,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/live/src/execution/manager.rs#L1265-L1301","documentation":"During reconciliation, NautilusTrader groups multiple FillReports that belong to the same external (orphan) order and synthesizes a single OrderStatusReport from them via create_orphan_fill_order_report. This ensure! guards the invariant that every fill in the group carries the same venue_order_id; a group whose fills disagree cannot be collapsed into one order report. It is an internal consistency check on venue-reported fill data.","triggerScenarios":"A batch of FillReports passed to create_orphan_fill_order_report (built during generate_order_status_reports / reconciliation from mass status fill reports) contains two fills with identical instrument/account/position grouping keys but different venue_order_id values, so the deduplication keying used to form the group did not match the fills' actual venue_order_id.","commonSituations":"Venue adapter bugs in fill-report grouping (e.g. grouping by trade ID or position instead of venue order ID); adapter updates after a venue changed its order-ID semantics; manually constructed or replayed reconciliation payloads mixing fills from different orders.","solutions":["Check how your venue adapter keys fill groups during reconciliation; fills must be grouped by (account_id, instrument_id, venue_order_id) before reaching create_orphan_fill_order_report.","Inspect the FillReport data from the venue's order/mass status report and verify venue_order_id is populated consistently for all fills of the same order.","Update or pin the adapter crate version so its grouping logic matches the venue's current ID semantics.","If the fills genuinely belong to different orders, ensure they are split into separate groups (or that one is not mislabeled as an orphan fill) instead of merged."],"exampleFix":"// before: grouping fills by position only\nlet group: Vec<&FillReport> = fills_by_position[position_id].clone();\n// after: group by venue order ID as well\nlet group: Vec<&FillReport> = fills_by_order[&venue_order_id].clone();","handlingStrategy":"validation","validationCode":"let venue_order_ids: std::collections::HashSet<_> = fills.iter().map(|f| f.venue_order_id).collect();\nassert!(venue_order_ids.len() == 1, \"fill group spans multiple venue order IDs: {:?}\", venue_order_ids);","typeGuard":"fn single_venue_order_group(fills: &[&FillReport]) -> bool {\n    fills.iter().all(|f| f.venue_order_id == fills[0].venue_order_id)\n}","tryCatchPattern":null,"preventionTips":["Always key reconciliation fill groups by (account_id, instrument_id, venue_order_id).","Log venue_order_id for every FillReport received so mis-grouping is visible before aggregation.","Keep venue adapter versions in sync with the deployed venue API semantics."],"tags":["reconciliation","rust","invariant-violation","fill-report"],"backgroundTag":"internal-invariant-violation","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"}