nautechsystems/nautilus_trader · error · anyhow::Error
fill group quantity is not positive
Error message
fill group quantity is not positive
What it means
After aggregating a fill group, the manager asserts via `anyhow::ensure!` that the summed fill quantity is strictly positive (`quantity > Decimal::ZERO`). This guards the invariant that a fill group representing an executed order must have positive total size; it is thrown when the sum is zero or negative. Zero-size or negative-quantity fills indicate malformed fill data, cancellation-as-fill handling bugs, or wrong-side quantity signs from the venue adapter.
Source
Thrown at crates/live/src/execution/manager.rs:1328
let quantity = quantity.checked_add(fill_quantity).ok_or_else(|| {
anyhow::anyhow!("fill quantity overflow while aggregating fill group")
})?;
let fill_notional = fill_quantity
.checked_mul(fill.last_px.as_decimal())
.ok_or_else(|| {
anyhow::anyhow!("fill notional overflow while aggregating fill group")
})?;
let notional = notional.checked_add(fill_notional).ok_or_else(|| {
anyhow::anyhow!("fill notional overflow while aggregating fill group")
})?;
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)View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the fills in the group and their `last_qty` signs and values
- Fix the adapter to report fill quantities as positive values regardless of reduce/long/short direction
- Filter out zero-quantity or non-fill (cancel/expire/reject) events before the fill-group aggregation path
- Verify the instrument's quantity handling matches venue semantics (e.g. contracts vs base units)
- Enable debug logging of raw venue fill messages and report the adapter bug if quantities look valid
Example fix
// before: venue reports reduction fills with negative qty let qty = raw_qty; // after: normalize to positive; side is carried by the order let qty = raw_qty.abs();
Defensive patterns
Strategy: validation
Validate before calling
fn fill_group_has_positive_quantity(fills: &[Fill]) -> bool {
!fills.is_empty()
&& fills.iter().fold(Decimal::ZERO, |acc, f| acc + f.last_qty.as_decimal())
> Decimal::ZERO
}
Type guard
fn is_positive_fill(f: &Fill) -> bool {
f.last_qty.as_decimal() > Decimal::ZERO
}
Try / catch
match manager.aggregate_fill_group(&fills) {
Ok(r) => handle(r),
Err(e) if e.to_string().contains("quantity is not positive") => {
log::error!("zero/negative aggregate fill quantity: {e}");
discard_or_reconcile_group(&fills);
}
Err(e) => return Err(e),
}
Prevention
- Filter zero-quantity and non-fill events (cancels, expires, rejects) before aggregation
- Normalize venue signed quantities to positive last_qty with side carried separately
- Test the adapter against reduce/close order fills to confirm positive quantity reporting
- Assert every fill in the group has positive last_qty in pre-aggregation validation
When it happens
Trigger: Processing a fill group where every fill has `last_qty == 0`, or where venue-reported quantities carry negative signs (some venues report position reductions as negative quantities) so the aggregate lands at or below zero; also possible when the fills list contains cancel/reject/expire records misrouted into the fill aggregation path.
Common situations: Exchange adapter not converting venue-side signed quantities to positive `last_qty` with a separate order-side; adapter emitting zero-quantity fill events for expired/canceled orders; replaying corrupted or truncated fill logs; reconciliation logic including reject/cancel events as fills.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- AX final-settlement fill must also be classified as a block
- generate_order_status_report requires instrument_id
- generate_order_status_report requires client_order_id or ven
- AX maintenance_margin_pct {maintenance_margin_pct} exceeds i
- AX regular fill has an empty order_id
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/2a8c5eed7113e035.
Report an issue: GitHub.