nautechsystems/nautilus_trader · error · anyhow::Error
order side differs across fill group
Error message
order side differs across fill group
What it means
When synthesizing an OrderStatusReport from a group of FillReports, the resulting report represents a single order with one side. This ensure! checks that every fill in the group has the same order_side; conflicting sides within one group means the fills cannot describe one coherent order and reconciliation aborts.
Source
Thrown at crates/live/src/execution/manager.rs:1291
for fill in fills.iter().skip(1) {
anyhow::ensure!(
fill.account_id == first.account_id,
"account ID differs across fill group"
);
anyhow::ensure!(
fill.instrument_id == first.instrument_id,
"instrument ID differs across fill group"
);
anyhow::ensure!(
fill.venue_order_id == first.venue_order_id,
"venue order ID differs across fill group"
);
anyhow::ensure!(
fill.client_order_id == first.client_order_id,
"client order ID differs across fill group"
);
anyhow::ensure!(
fill.order_side == first.order_side,
"order side differs across fill group"
);
anyhow::ensure!(
fill.venue_position_id == first.venue_position_id,
"venue position ID differs across fill group"
);
}
anyhow::ensure!(
first.instrument_id == instrument.id(),
"instrument metadata does not match fill group"
);
let (quantity, notional) = fills.iter().try_fold(
(Decimal::ZERO, Decimal::ZERO),
|(quantity, notional), fill| {
let fill_quantity = fill.last_qty.as_decimal();View on GitHub (pinned to 18893faf8b)
Solutions
- Group fills by the full order key (account, instrument, venue_order_id, client_order_id, order_side) instead of instrument or position alone.
- Check the adapter's fill-report parsing to confirm order_side is read from the correct venue field and not defaulted or inferred inconsistently.
- Split mixed-side groups into separate orphan orders, one per side, before calling create_orphan_fill_order_report.
- Validate the venue's raw execution reports: if the venue itself reports conflicting sides for one order ID, capture that payload and report it to the adapter maintainers.
Example fix
// before: key = (account, instrument) let key = (account_id, instrument_id); // after: include side in the group key let key = (account_id, instrument_id, venue_order_id, order_side);
Defensive patterns
Strategy: validation
Validate before calling
let sides: std::collections::HashSet<_> = fills.iter().map(|f| f.order_side).collect();
assert!(sides.len() == 1, "fill group has mixed order sides: {:?}", sides); Type guard
fn single_side_group(fills: &[&FillReport]) -> bool {
fills.iter().all(|f| f.order_side == fills[0].order_side)
} Prevention
- Include order_side in the fill-group key, especially on hedging-mode accounts.
- Confirm the adapter reads order_side from the venue's canonical field, not an inferred default.
- Split opposite-side fills into separate orphan orders before aggregation.
When it happens
Trigger: Two or more FillReports grouped as belonging to the same order (same account, instrument, venue/client order ID) carry different OrderSide values (e.g. one BUY and one SELL), such as when closes of opposite-side positions are mis-grouped together.
Common situations: Hedged-mode venues where opposite-side fills share a position but belong to different orders; adapter grouping fills by account+instrument instead of by order; corrupted or hand-edited replayed fill data.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- {evidence} side {actual} does not match known order side {ex
- venue order ID differs across fill group
- client order ID differs across fill group
- venue position ID differs across fill group
- Invalid order side: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/cb52925784bf02bc.
Report an issue: GitHub.