nautechsystems/nautilus_trader · error · anyhow::Error

fill {} position ID {venue_position_id} conflicts with cache

Error message

fill {} position ID {venue_position_id} conflicts with cached order position {mapped_position_id}

What it means

In hedge mode (hedging context), a fill report's venue_position_id is compared to the position ID mapped to the cached order. If they differ, the fill is attributed to a different position than the order's mapping, so the manager rejects it to keep position hedging consistent.

Source

Thrown at crates/live/src/execution/manager.rs:2861

                "fill {} conflicts with cached order {}",
                report.trade_id,
                order.client_order_id(),
            );
        }

        let hedge_context = report.venue_position_id.is_some()
            || venue_reports
                .iter()
                .any(|venue_report| venue_report.venue_position_id.is_some());
        let mapped_position_id = client_order_id
            .and_then(|client_order_id| cache.position_id(&client_order_id))
            .copied();

        if hedge_context
            && let (Some(venue_position_id), Some(mapped_position_id)) =
                (report.venue_position_id, mapped_position_id)
        {
            anyhow::ensure!(
                venue_position_id == mapped_position_id,
                "fill {} position ID {venue_position_id} conflicts with cached order position {mapped_position_id}",
                report.trade_id,
            );
        }

        if let Some(order) = order
            && Self::has_active_inferred_fill(&order)?
        {
            return Ok(PositionFillReportPreparation::InferredOverlap);
        }

        if !hedge_context {
            return Ok(PositionFillReportPreparation::Ready);
        }

        if report.venue_position_id.is_some() {
            return Ok(PositionFillReportPreparation::Ready);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Make the adapter return the venue_position_id consistently for all fills of the same position
  2. Verify the node is configured with the correct netting vs hedging account type matching the venue
  3. Re-reconcile cached position mappings with venue state after reconnects
  4. Purge stale cache entries whose position mappings no longer match the venue

Example fix

// before (synthetic per-fill position ID)
report.venue_position_id = Some(VenuePositionId::new(uuid::Uuid::new_v4().to_string()));
// after (venue-reported position, reused across fills)
report.venue_position_id = venue_fill.position_id.map(VenuePositionId::from);
Defensive patterns

Strategy: validation

Validate before calling

if hedge_context {
    if let (Some(vp), Some(mp)) = (report.venue_position_id, cache.mapped_position_id(&client_order_id)) {
        assert_eq!(vp, mp);
    }
}

Type guard

fn position_mapping_consistent(report: &TradeReport, order: &OrderAny) -> bool {
    report.venue_position_id.map_or(true, |p| {
        order.position_id().map_or(true, |op| op == p)
    })
}

Prevention

When it happens

Trigger: A fill arrives with venue_position_id P1 while the cached order's position mapping says P2, under a hedging account. Happens when the venue reassigns positions, when the adapter invents position IDs per fill, or when cache position mappings are stale after restart/reconciliation.

Common situations: Hedging mode with venues that aggregate fills into positions differently than the adapter assumed; netting venue behind a hedging-configured node; adapter generating synthetic position IDs that drift from cached ones.

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/616bb289e2f1e3e6. Report an issue: GitHub.