nautechsystems/nautilus_trader · error · anyhow::Error

Persisted swap strategy does not match restored order

Error message

Persisted swap strategy does not match restored order

What it means

restore_swap_plan (crates/adapters/blockchain/src/execution/client.rs:849) requires the persisted intent's strategy_id to match the strategy of the restored order named by its client order id. A mismatch means the durable swap record was spawned by a different strategy than the live order - reconciliation aborts so fills are never attributed to the wrong strategy. Strategy attribution flows into every emitted order event, hence the strict equality check.

Source

Thrown at crates/adapters/blockchain/src/execution/client.rs:849

                    intent.id
                )
            })?;
        let instrument_id = InstrumentId::from_str(
            intent
                .instrument_id
                .as_deref()
                .ok_or_else(|| anyhow::anyhow!("Persisted swap intent has no instrument ID"))?,
        )?;
        anyhow::ensure!(
            order.instrument_id() == instrument_id,
            "Persisted swap instrument {instrument_id} does not match restored order instrument {}",
            order.instrument_id()
        );
        anyhow::ensure!(
            intent.trader_id.as_deref() == Some(order.trader_id().as_str()),
            "Persisted swap trader does not match restored order"
        );
        anyhow::ensure!(
            intent.strategy_id.as_deref() == Some(order.strategy_id().as_str()),
            "Persisted swap strategy does not match restored order"
        );
        anyhow::ensure!(
            intent.account_id.as_deref() == Some(self.core.account_id.as_str()),
            "Persisted swap account does not match execution client account"
        );

        let pool = self.resolve_pool(&instrument_id)?;
        let pool_address = Address::from_str(
            intent
                .pool_address
                .as_deref()
                .ok_or_else(|| anyhow::anyhow!("Persisted swap intent has no pool address"))?,
        )?;
        anyhow::ensure!(
            pool.address == pool_address,
            "Persisted pool {pool_address} does not match restored pool {}",

View on GitHub (pinned to 2114cf6f76)

Solutions

  1. Compare the intent's strategy_id with the current strategy configuration and the restored order's strategy.
  2. Verify the on-chain outcome of the intent's transaction and mark the intent resolved to unblock reconciliation.
  3. Clear active intents before changing strategy identifiers.
  4. Keep strategy ids stable for the lifetime of any intent they create.
Defensive patterns

Strategy: try-catch

Validate before calling

-- Pre-flight: intents recorded under a strategy other than current config
SELECT id, strategy_id, status
FROM execution_intent
WHERE status IN ('prepared', 'signed', 'submitted');

Type guard

fn is_strategy_mismatch(e: &anyhow::Error) -> bool {
    e.to_string().contains("Persisted swap strategy does not match restored order")
}

Try / catch

if let Err(e) = client.connect().await {
    if is_strategy_mismatch(&e) {
        // strategy attribution conflict: resolve the stale intent before trading
        log::error!("swap intent belongs to a different strategy: {e}");
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Strategy configuration changed across restarts while an intent persisted by the old strategy is still active; the order cache restored under a different strategy id; a client order id reused by a different strategy in a later session.

Common situations: Renaming strategies in config between runs; promoting a strategy id from paper to live trading with stale intents in the store.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21). Data as JSON: /api/errors/32115d5460dbed23. Report an issue: GitHub.