{"record":{"id":"57e0bbec27f3029b","repo":"nautechsystems/nautilus_trader","slug":"cached-pool-does-not-match-its-deployment-manif","errorCode":null,"errorMessage":"Cached pool {} does not match its deployment manifest identity","messagePattern":"Cached pool (.+?) does not match its deployment manifest identity","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":4135,"sourceCode":"    plan: &SwapPlan,\n    manifest: &BlockchainDeploymentManifest,\n) -> anyhow::Result<Address> {\n    let matching = manifest\n        .pools\n        .iter()\n        .filter(|pool| Address::from_str(&pool.address).ok() == Some(plan.pool_address))\n        .collect::<Vec<_>>();\n    anyhow::ensure!(\n        matching.len() == 1,\n        \"Pool {} does not have exactly one deployment manifest definition\",\n        plan.pool_address\n    );\n    let pool = matching[0];\n    let token0 = Address::from_str(&pool.token0)?;\n    let token1 = Address::from_str(&pool.token1)?;\n    let factory = Address::from_str(&pool.factory)?;\n    let quote_contract = Address::from_str(&pool.quote_contract)?;\n    anyhow::ensure!(\n        token0 == plan.pool.token0.address\n            && token1 == plan.pool.token1.address\n            && pool.fee == plan.pool.fee.expect(\"validated pool fee\")\n            && factory == plan.factory,\n        \"Cached pool {} does not match its deployment manifest identity\",\n        plan.pool_address\n    );\n\n    for token in [&plan.pool.token0, &plan.pool.token1] {\n        let identities = manifest\n            .tokens\n            .iter()\n            .filter(|identity| Address::from_str(&identity.address).ok() == Some(token.address))\n            .collect::<Vec<_>>();\n        anyhow::ensure!(\n            identities.len() == 1,\n            \"Token {} does not have exactly one deployment manifest identity\",\n            token.address","sourceCodeStart":4117,"sourceCodeEnd":4153,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L4117-L4153","documentation":"In `validate_manifest_pool` (crates/adapters/blockchain/src/execution/client.rs:4135), after resolving the manifest entry, its token0/token1 addresses, fee, and factory are compared against the pool identity cached in the plan. Any mismatch means the client's cached pool does not correspond to the on-chain deployment described by the manifest, so execution is refused to prevent swapping on the wrong pool.","triggerScenarios":"A swap plan is built from a cached pool whose token0.address, token1.address, fee, or factory differs from the manifest entry for the same pool address — a cache populated from a different chain/environment, a stale cache after a pool redeploy, or a fee-tier mismatch.","commonSituations":"Client restarted with a manifest for a different deployment environment while reusing a persisted cache; a pool was redeployed at the same address after a fork/testnet reset; fee tier changed in config but the cache holds the old fee; copy-pasted token addresses between mainnet and testnet.","solutions":["Invalidate/clear the cached pool and re-resolve it from the current deployment manifest before retrying the swap.","Align the manifest and the cache source to the same environment/chain; regenerate the cache from that manifest.","Fix the mismatched field (token0/token1/fee/factory) in either the manifest or the pool-cache builder so both describe the same pool.","If the pool was redeployed, update the manifest address/fields and rebuild all cached pools referencing the old deployment."],"exampleFix":"// before: cache built from stale environment\nlet pool = pool_cache.get(plan.pool_address); // fee: 500\n// manifest says fee: 3000 for this address -> mismatch\n// after: rebuild cache from the active manifest\nlet pool = resolve_pool_from_manifest(&manifest, plan.pool_address)?;\nanyhow::ensure!(pool.fee == manifest_fee, \"cache stale; rebuilt from manifest\");","handlingStrategy":"validation","validationCode":"let m = manifest.pools.iter()\n    .find(|p| Address::from_str(&p.address).ok() == Some(pool.address))\n    .ok_or(\"pool missing from manifest\")?;\nif Address::from_str(&m.token0)? != pool.token0.address\n    || Address::from_str(&m.token1)? != pool.token1.address\n    || m.fee != pool.fee\n    || Address::from_str(&m.factory)? != pool.factory {\n    return Err(\"cached pool identity does not match manifest\");\n}","typeGuard":"fn pool_matches_manifest(pool: &CachedPool, m: &ManifestPool) -> anyhow::Result<bool> {\n    Ok(Address::from_str(&m.token0)? == pool.token0.address\n        && Address::from_str(&m.token1)? == pool.token1.address\n        && m.fee == pool.fee\n        && Address::from_str(&m.factory)? == pool.factory)\n}","tryCatchPattern":"match client.execute_swap(plan).await {\n    Err(e) if e.to_string().contains(\"does not match its deployment manifest identity\") => {\n        // invalidate the pool cache and re-resolve from the manifest\n    }\n    other => other?,\n}","preventionTips":["Rebuild the pool cache whenever the manifest version/checksum changes.","Key caches by (chain_id, pool_address) so cross-environment reuse cannot happen.","Compare fee and factory — not just addresses — whenever a pool is loaded from cache.","Clear persisted caches after testnet resets or pool redeploys."],"tags":["blockchain","cache-staleness","manifest","identity-mismatch"],"backgroundTag":"checksum-mismatch","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}