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

  1. Verify commission_voided equals the commission originally recorded with the fill (same amount and currency).
  2. Make void handling idempotent so a duplicated void event is skipped instead of re-distributing commission.
  3. Restore cache state from a consistent snapshot so all position commission fragments are present before voiding.
  4. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/9d592f808a88301e. Report an issue: GitHub.