{"record":{"id":"4b7b0b1676d761a3","repo":"nautechsystems/nautilus_trader","slug":"restored-pool-instrument-id-has-no-fee","errorCode":null,"errorMessage":"Restored pool {instrument_id} has no fee","messagePattern":"Restored pool (.+?) has no fee","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":878,"sourceCode":"            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,\n            0,\n            &quote_token.name,\n            CurrencyType::Crypto,\n        )?;\n        let token_in = pool.get_base_token().address;\n        let token_out = quote_token.address;\n\n        Ok(SwapPlan {\n            order,\n            quote_currency,\n            pool,\n            instrument_id,\n            pool_address,","sourceCodeStart":860,"sourceCodeEnd":896,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/2114cf6f761429e0adb5ca9596fcd7b895b16011/crates/adapters/blockchain/src/execution/client.rs#L860-L896","documentation":"restore_swap_plan (crates/adapters/blockchain/src/execution/client.rs:878) reads pool.fee as a U24 for the restored pool; when the cached Pool for the instrument has fee: None the reconciliation aborts. The fee is required to rebuild swap calldata and quote-token math for the restored instrument. A fee-less pool means the pool was registered or restored without its fee parameter - incomplete instrument metadata rather than an on-chain anomaly.","triggerScenarios":"The pool for the intent's instrument exists in the cache but was registered without a fee tier (custom registration path or data source that omits fee); a persisted pool row lacking the fee restored into memory; reconciling a swap intent against such a pool at connect().","commonSituations":"Custom pool ingestion pipelines that skip the fee field; pools added through config with only an address; database pool rows written before fee capture existed.","solutions":["Check how the pool for the failing instrument got registered and ensure its fee tier (e.g. 3000 for 0.3%) is provided.","Refresh the pool from an authoritative source so fee is populated, then reconnect.","If the pool row in Postgres lacks the fee, update it through the ingestion source (not by hand-editing live rows if avoidable).","Resolve or complete the active intent once pool metadata is correct."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Rust: verify instrument pools carry a fee tier before booting reconciliation\n// (resolve_pool must produce pools with Some(fee), e.g. 500/3000/10000 for Uniswap V3)\nfor instrument in active_swap_instruments() {\n    let pool = client.resolve_pool(&instrument)?;\n    anyhow::ensure!(pool.fee.is_some(), \"pool for {instrument} lacks a fee tier\");\n}","typeGuard":"fn is_pool_missing_fee(e: &anyhow::Error) -> bool {\n    e.to_string().contains(\"has no fee\")\n}","tryCatchPattern":"if let Err(e) = client.connect().await {\n    if is_pool_missing_fee(&e) {\n        // instrument metadata incomplete: refresh pool registration with fee tiers\n        log::error!(\"pool registered without fee tier; fix ingestion then reconnect: {e}\");\n    }\n    return Err(e);\n}","preventionTips":["Require the fee tier in whatever pipeline registers pools into the cache.","Validate pool metadata (address + fee) when instruments are configured.","Refresh pool rows from an authoritative source before reconciling active swap intents."],"tags":["reconciliation","pool","fee-tier","instrument-metadata","rust"],"backgroundTag":"missing-required-field","analyzedSha":"2114cf6f761429e0adb5ca9596fcd7b895b16011","analyzedAt":"2026-08-21T11:28:30.864Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}