nautechsystems/nautilus_trader · error
non-empty fill group
Error message
non-empty fill group
What it means
When synthesizing an `OrderStatusReport` from a group of fills, the live execution manager computes the earliest fill timestamp with `fills.iter().map(...).min().expect("non-empty fill group")`. `Iterator::min` returns `None` for an empty iterator, so the `.expect` panics. The invariant is that every caller passes at least one fill; hitting this panic means that assumption was violated.
Source
Thrown at crates/live/src/execution/manager.rs:1342
Ok::<_, anyhow::Error>((quantity, notional))
},
)?;
anyhow::ensure!(
quantity > Decimal::ZERO,
"fill group quantity is not positive"
);
let order_qty = Quantity::from_decimal_dp(quantity, instrument.size_precision())?;
let avg_px = notional
.checked_div(quantity)
.ok_or_else(|| anyhow::anyhow!("fill group average price is not representable"))?;
let ts_accepted = fills
.iter()
.map(|fill| fill.ts_event)
.min()
.expect("non-empty fill group");
let ts_last = fills
.iter()
.map(|fill| fill.ts_event)
.max()
.expect("non-empty fill group");
let ts_init = fills
.iter()
.map(|fill| fill.ts_init)
.max()
.expect("non-empty fill group");
let report = OrderStatusReport::new(
first.account_id,
first.instrument_id,
first.client_order_id,
first.venue_order_id,View on GitHub (pinned to 18893faf8b)
Solutions
- Guard the call site: bail or return early when `fills.is_empty()` before constructing the report
- Replace the `expect` with `ok_or_else` + `?` so an empty group yields a handled error instead of a panic
- Log the instrument_id/client_order_id that produced the empty group to find the upstream filter dropping fills
- Review dedup/skip logic in fill-group construction for over-aggressive removal
Example fix
// before
let ts_accepted = fills.iter().map(|f| f.ts_event).min().expect("non-empty fill group");
// after
anyhow::ensure!(!fills.is_empty(), "cannot build OrderStatusReport from empty fill group");
let ts_accepted = fills.iter().map(|f| f.ts_event).min().unwrap(); Defensive patterns
Strategy: type-guard
Validate before calling
if fills.is_empty() {
return Err(anyhow::anyhow!("empty fill group; cannot build OrderStatusReport"));
} Type guard
fn non_empty(fills: &[OrderFillReport]) -> Option<&[OrderFillReport]> {
(!fills.is_empty()).then_some(fills)
} Try / catch
match result_of_report_build { Ok(r) => r, Err(e) => log::error!("report build failed: {e}") } Prevention
- Never call the report builder with a filtered collection without re-checking emptiness
- Assert group non-emptiness right after fill-group construction
- Log upstream filters that remove fills to catch over-filtering early
When it happens
Trigger: Reaching the report-building code with an empty `fills` collection — e.g. an `OrderStatusReport` generated during reconciliation or `generate_order_status_reports` where a matched fill group was filtered/deduplicated down to zero elements.
Common situations: Custom reconciliation logic passing empty fill vectors; an upstream filter removing all fills of a group; a venue status response containing zero fills while the code still attempts report construction.
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
- Close command should not be drained
- Flush command should not be drained
- Order invariant violated: first event must be OrderInitializ
- venue-leg fills are negative
- validated proxy target hash
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/2676443e1b27b050.
Report an issue: GitHub.