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
During swap-plan restoration after a restart, the blockchain execution client verifies that the trader_id persisted with the execution intent row matches the trader_id of the restored order from the cache. A mismatch means the durable intent and the in-memory order came from different trader identities, so reconciliation is aborted to avoid attributing a persisted swap to the wrong trader. This is a safety invariant: replaying an intent against an order with a different owner could submit trades under incorrect identity.
Solutions
- Make sure the trader_id configured for this node matches the trader_id of the orders and intents in the durable store (revert the config change or use the original trader_id).
- Point the cache database at the durable store belonging to this trader (separate DB file/namespace per trader_id) instead of a shared one.
- If the intent row is corrupt or stale, clear/resolve the active execution intent for this wallet/chain so reconciliation skips it.
- Ensure orders are restored from the same cache snapshot that produced the intents; do not mix cache snapshots across traders.
Example fix
// before (config changed between runs) trader_id = "TRADER-002" // after (match the trader that persisted the intents) trader_id = "TRADER-001"
Defensive patterns
Strategy: validation
Validate before calling
// Before starting reconciliation, confirm trader identity matches the persisted intents
let intent_trader: Option<&str> = intent.trader_id.as_deref();
let order_trader = order.trader_id().as_str();
if intent_trader != Some(order_trader) {
eprintln!("intent trader {intent_trader:?} != order trader {order_trader}; fix trader_id config before connecting");
} Type guard
fn trader_matches(intent: &ExecutionIntentRow, order: &OrderAny) -> bool {
intent.trader_id.as_deref() == Some(order.trader_id().as_str())
} Prevention
- Keep trader_id stable across restarts; treat changing it as requiring a clean durable store
- Use a separate database per trader_id
- Never share one durable store between trader instances
- Audit persisted intent rows for NULL identity columns before upgrade
When it happens
Trigger: restore_swap_plan (called from reconcile_unresolved_execution or build_execution_verification_migration) loads an ExecutionIntentRow whose intent.trader_id is None or differs from order.trader_id() for the order identified by intent.client_order_id. Happens when the cache is restored from a different instance/trader_id than the one that persisted the intent (e.g. trader_id changed in config between runs), or the intent row was written by another trader sharing the same database.
Common situations: Changing the trader_id in the node configuration after intents were persisted; pointing multiple trader instances at one shared durable store; restoring a cache snapshot from a different deployment; a NULL trader_id column on an old or hand-edited intent row.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Execution intent has no persisted signed transaction bytes
- Persisted swap account does not match execution client…
- Persisted swap strategy does not match restored order
- Cached pool does not match its deployment manifest identity
- Canonical nonce advanced without an authenticated signer…
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/18721d225c480b12.
Report an issue: GitHub.
Appendix: source
Thrown at crates/adapters/blockchain/src/execution/client.rs:1049
.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 18893faf8b)