nautechsystems/nautilus_trader · error
position fragments do not cover voided commission for fill {
Error message
position fragments do not cover voided commission for fill {} What it means
After distributing the voided commission across position fragments, the engine requires the remaining commission to be exactly zero. If the recorded per-position commissions total less than the commission being voided, the leftover amount cannot be attributed to any position and the engine aborts the void with this error to keep commission accounting exact.
Source
Thrown at crates/execution/src/engine/mod.rs:3964
);
let removed_raw = remaining_commission.raw.abs().min(commission.raw.abs());
let removed = Money::from_raw(
removed_raw * remaining_commission.raw.signum(),
remaining_commission.currency,
);
allocations
.entry(*position_id)
.and_modify(|allocation| {
allocation.1 = Some(
allocation
.1
.map_or(removed, |commission| commission + removed),
);
})
.or_insert((Quantity::zero(event.voided_qty.precision), Some(removed)));
remaining_commission = remaining_commission - removed;
}
anyhow::ensure!(
remaining_commission.is_zero(),
"position fragments do not cover voided commission for fill {}",
event.trade_id
);
}
let mut corrected_positions = Vec::new();
for (position_id, (voided_qty, commission_voided)) in allocations {
if voided_qty.is_zero() {
anyhow::bail!(
"commission-only position correction requires authoritative reconciliation for fill {}",
event.trade_id
);
}
let mut position = self
.cache
.borrow()View on GitHub (pinned to 18893faf8b)
Solutions
- Verify commission_voided equals the commission originally recorded with the fill (same amount and currency).
- Make void handling idempotent so a duplicated void event is skipped instead of re-distributing commission.
- Restore cache state from a consistent snapshot so all position commission fragments are present before voiding.
- Trace the distribution loop at crates/execution/src/engine/mod.rs (~line 3946-3962) and log remaining_commission after each fragment to find the shortfall.
Example fix
// before: void event carries recalculated (larger) commission
let voided = Money::new(3.0, Currency::USD()); // recorded was 2.0
engine.apply_fill_void(FillVoided { commission_voided: Some(voided), .. })?;
// after: use the originally booked commission
let voided = recorded_fill.commission; // 2.0 USD
engine.apply_fill_void(FillVoided { commission_voided: Some(voided), .. })?; Defensive patterns
Strategy: validation
Validate before calling
// voided commission must equal the commission originally booked with the fill
let booked = recorded_fill(event.trade_id).commission;
if let Some(voided) = &event.commission_voided {
assert_eq!(voided, &booked, "voided commission must equal booked commission");
} Try / catch
match engine.apply_fill_void(event) {
Err(e) if e.to_string().contains("do not cover voided commission") => {
log::error!("commission under-covered for fill {}: {e}", event.trade_id);
// stop replay; compare event.commission_voided against the fill record
}
other => other?,
} Prevention
- Copy commission_voided from the original fill record, never recompute it at void time.
- Skip already-processed void events to avoid double distribution.
- Keep commission records and position fragments in the same snapshot.
When it happens
Trigger: Applying a fill void where commission_voided exceeds the sum of commissions recorded against the involved positions — e.g. the void event carries a stale or recomputed commission amount, or a position's commission fragment was already consumed by an earlier void.
Common situations: Replaying void events twice; commission recalculation (e.g. tiered fee change) making the voided amount larger than originally booked; partial cache restores dropping some position commission records.
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
- position fragments do not cover voided quantity for fill {}
- responsible execution client is unavailable
- position commission currency differs for fill {}
- position {position_id} is not cached
- Order for {} not found to determine position ID
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/9d592f808a88301e.
Report an issue: GitHub.