{"record":{"id":"f4e62543dbb267fb","repo":"nautechsystems/nautilus_trader","slug":"persisted-pool-pool-address-does-not-match-resto","errorCode":null,"errorMessage":"Persisted pool {pool_address} does not match restored pool {}","messagePattern":"Persisted pool (.+?) does not match restored pool (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":865,"sourceCode":"            \"Persisted swap trader does not match restored order\"\n        );\n        anyhow::ensure!(\n            intent.strategy_id.as_deref() == Some(order.strategy_id().as_str()),\n            \"Persisted swap strategy does not match restored order\"\n        );\n        anyhow::ensure!(\n            intent.account_id.as_deref() == Some(self.core.account_id.as_str()),\n            \"Persisted swap account does not match execution client account\"\n        );\n\n        let pool = self.resolve_pool(&instrument_id)?;\n        let pool_address = Address::from_str(\n            intent\n                .pool_address\n                .as_deref()\n                .ok_or_else(|| anyhow::anyhow!(\"Persisted swap intent has no pool address\"))?,\n        )?;\n        anyhow::ensure!(\n            pool.address == pool_address,\n            \"Persisted pool {pool_address} does not match restored pool {}\",\n            pool.address\n        );\n        let amount_in = U256::from_str(\n            intent\n                .amount_in\n                .as_deref()\n                .ok_or_else(|| anyhow::anyhow!(\"Persisted swap intent has no input amount\"))?,\n        )?;\n        let fee = U24::try_from(\n            pool.fee\n                .ok_or_else(|| anyhow::anyhow!(\"Restored pool {instrument_id} has no fee\"))?,\n        )?;\n        let quote_token = pool.get_quote_token();\n        let quote_currency = Currency::new_checked(\n            &quote_token.symbol,\n            quote_token.decimals,","sourceCodeStart":847,"sourceCodeEnd":883,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/2114cf6f761429e0adb5ca9596fcd7b895b16011/crates/adapters/blockchain/src/execution/client.rs#L847-L883","documentation":"restore_swap_plan (crates/adapters/blockchain/src/execution/client.rs:865) resolves the pool for the intent's instrument via resolve_pool and requires the persisted intent's pool_address to equal it. A mismatch means the pool mapping configured in the restarted client differs from the pool the original swap actually routed through - completing reconciliation would attribute the swap to the wrong pool. The check keeps durable execution history consistent with live pool topology.","triggerScenarios":"Pool discovery or allowlist configuration changed between the run that persisted the intent and the reconciling run (e.g. instrument now maps to a different pool address); a pool migrated/renumbered (fee tier change recorded as a different pool) while an old intent stayed active; duplicate pools for the same instrument with different mappings across configs.","commonSituations":"Editing pool allowlists or instrument-to-pool mappings between restarts; deployments that rediscover pools in a different order and pin different addresses.","solutions":["Compare the intent's pool_address with the pool your current configuration resolves for that instrument.","Restore the pool mapping that was in effect when the swap was signed, reconnect, and let reconciliation complete.","Once the intent is finalized, update configuration deliberately.","Keep pool mappings deterministic (pin pool addresses in config) for any instrument with active intents."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"-- Pre-flight: compare each active intent's pool with your current mapping\nSELECT i.id, i.instrument_id, i.pool_address, status\nFROM execution_intent i\nWHERE i.status IN ('prepared', 'signed', 'submitted')\n  AND i.purpose = 'swap';\n-- verify each pool_address still equals the pool your config resolves for instrument_id","typeGuard":"fn is_pool_mismatch(e: &anyhow::Error) -> bool {\n    e.to_string().contains(\"does not match restored pool\")\n}","tryCatchPattern":"if let Err(e) = client.connect().await {\n    if is_pool_mismatch(&e) {\n        // pool topology changed under an active intent: restore the original mapping\n        // or resolve the intent on-chain before trading\n        log::error!(\"pool mapping conflicts with persisted swap intent: {e}\");\n    }\n    return Err(e);\n}","preventionTips":["Pin pool addresses deterministically in config instead of relying on discovery order.","Freeze pool-mapping changes while any intent for the instrument is active.","Diff pool mappings across restarts as part of deployment checks."],"tags":["reconciliation","pool","state-mismatch","configuration","rust"],"backgroundTag":"persisted-state-mismatch","analyzedSha":"2114cf6f761429e0adb5ca9596fcd7b895b16011","analyzedAt":"2026-08-21T11:28:30.864Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}