nautechsystems/nautilus_trader · error · anyhow::Error

incompatible cached {position_side:?} position IDs for {inst

Error message

incompatible cached {position_side:?} position IDs for {instrument_id}: {}; expected {venue_position_id}

What it means

When use_position_ids=true, the adapter canonically names each position ID after the venue position (instrument + hedge side). ensure_cached_position_id_compatible scans the Nautilus cache for open positions of the same instrument/side/account whose IDs differ from the canonical venue ID; any such legacy or synthetic IDs would break identity tracking, so the error lists them and aborts.

Source

Thrown at crates/adapters/binance/src/futures/execution.rs:1087

        position_side: PositionSide,
        venue_position_id: PositionId,
    ) -> anyhow::Result<()> {
        let cache = self.core.cache();
        let mut incompatible_ids: Vec<_> = cache
            .positions_open(
                Some(&BINANCE_VENUE),
                Some(&instrument_id),
                None,
                Some(&self.core.account_id),
                Some(position_side),
            )
            .into_iter()
            .filter(|position| position.id != venue_position_id)
            .map(|position| position.id.to_string())
            .collect();
        incompatible_ids.sort_unstable();

        anyhow::ensure!(
            incompatible_ids.is_empty(),
            "incompatible cached {position_side:?} position IDs for {instrument_id}: {}; expected {venue_position_id}",
            incompatible_ids.join(", "),
        );
        Ok(())
    }

    async fn generate_open_order_status_reports(
        &self,
        instrument_id: Option<InstrumentId>,
        ts_init: UnixNanos,
    ) -> anyhow::Result<Vec<OpenOrderStatusReport>> {
        if let Some(instrument_id) = instrument_id
            && self
                .http_client
                .instrument_reconciliation(&instrument_id)
                .is_none()
        {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Clear stale cached open positions for the affected instrument (purge/rebuild the cache database) so only venue position IDs remain.
  2. Run one clean reconciliation with use_position_ids=true from an empty cache.
  3. Keep use_position_ids setting stable across restarts; do not toggle between virtual and venue position IDs on the same cache.
  4. Close the conflicting positions and let the adapter recreate them with canonical venue IDs.
Defensive patterns

Strategy: validation

Validate before calling

let stale: Vec<_> = cache.positions_open(Some(&BINANCE_VENUE), Some(&instrument_id), None, Some(&account_id), Some(side))
    .into_iter().filter(|p| p.id != expected_venue_id).collect();
if !stale.is_empty() { /* purge or re-create cached positions before reconciliation */ }

Prevention

When it happens

Trigger: Reconciliation (create_position_report / generate_position_status_reports) finds open cached positions for an instrument+side that were created before use_position_ids was enabled, or by a different run with virtual (non-venue) position IDs.

Common situations: Restarting a trader after flipping use_position_ids=true on a cache populated with virtual position IDs; mixing strategy-generated PositionIds with venue IDs for the same instrument; replay/backtest cache reused in live trading.

Related errors


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