nautechsystems/nautilus_trader · error · anyhow::Error

Persisted swap trader does not match restored order

Error message

Persisted swap trader does not match restored order

What it means

restore_swap_plan (crates/adapters/blockchain/src/execution/client.rs:845) verifies the persisted intent's trader_id equals the trader of the order restored under its client order id. A mismatch means the durable swap record belongs to a different trader than the live order - the client refuses to reconcile because completing it would attribute executions to the wrong account holder. The check guarantees trader attribution of emitted fills matches the persisted intent exactly.

Source

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

            .try_order_owned(&client_order_id)
            .with_context(|| {
                format!(
                    "Cannot reconcile swap intent {} because order {client_order_id} is not restored",
                    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"))?,

View on GitHub (pinned to 2114cf6f76)

Solutions

  1. Check the intent row's trader_id versus the current trader configuration and the restored order's trader.
  2. Resolve the orphaned intent on-chain (verify the transaction outcome) and mark it resolved so reconciliation no longer picks it up.
  3. When changing trader configuration, drain or clear active intents first.
  4. Scope intents per trader in the database to avoid cross-trader collisions.
Defensive patterns

Strategy: try-catch

Validate before calling

-- Pre-flight: intents recorded under a trader other than the one you will boot
SELECT id, trader_id, status
FROM execution_intent
WHERE status IN ('prepared', 'signed', 'submitted');

Type guard

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

Try / catch

if let Err(e) = client.connect().await {
    if is_trader_mismatch(&e) {
        // attribution conflict: boot the trader that owns the intent, or resolve it first
        log::error!("swap intent belongs to a different trader: {e}");
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Trader configuration changed between the run that persisted the intent and the restarted run reconciling it; an order cache restored under a different trader_id while the old intent row remains active; intents written by a prior deployment under another trader account.

Common situations: Re-keying a deployment from a test trader to a production trader without clearing durable intents; multi-trader setups sharing one Postgres store.

Related errors


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