{"record":{"id":"8b3b62fffefa511f","repo":"nautechsystems/nautilus_trader","slug":"router-router-is-not-in-the-configured-router-a","errorCode":null,"errorMessage":"Router {router} is not in the configured `router_addresses` allowlist","messagePattern":"Router (.+?) is not in the configured `router_addresses` allowlist","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":733,"sourceCode":"    ///\n    /// This is an explicit operator operation; it never runs inside `submit_order`.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the router or token fails policy and deployment checks, a nonzero\n    /// allowance was not cleared first, approval simulation returns false or malformed data, the\n    /// client is not connected, another transaction is in flight, no durable store is configured,\n    /// the resulting allowance differs from the target, or any RPC, signing, persistence, or\n    /// broadcast step fails. A persistence failure after signing, or a failed postcondition after\n    /// finality, leaves the in-flight slot occupied.\n    pub async fn approve(\n        &mut self,\n        token: Address,\n        amount: U256,\n        router: Address,\n    ) -> anyhow::Result<B256> {\n        if !self.router_addresses.contains(&router) {\n            anyhow::bail!(\"Router {router} is not in the configured `router_addresses` allowlist\");\n        }\n\n        if !amount.is_zero()\n            && !self\n                .transaction_limits\n                .allowed_token_pairs\n                .iter()\n                .any(|(token_in, _)| *token_in == token)\n        {\n            anyhow::bail!(\n                \"Token {token} is not an input token in the configured `allowed_token_pairs`\"\n            );\n        }\n\n        self.ensure_transaction_ready(TransactionPurpose::Approve)?;\n\n        let approval_amount = if amount.is_zero() {\n            U256::ZERO","sourceCodeStart":715,"sourceCodeEnd":751,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L715-L751","documentation":"BlockchainExecutionClient::approve rejects any ERC-20 approval whose spender (router) address is not present in the client's configured `router_addresses` allowlist. This is a deliberate policy guard: the adapter only ever grants spending allowance to explicitly vetted router contracts, preventing token approvals to unexpected or malicious contracts. If the router address passed to approve() is not exactly listed in the configuration, the call bails before any transaction is built.","triggerScenarios":"Calling `client.approve(token, amount, router)` where `router` is not an exact member of `self.router_addresses` (the allowlist supplied at client construction / config). Also triggered by case-sensitive mismatch: the check uses Address equality, so a differently checksummed-but-same address still matches (Address normalizes), but a genuinely different router deployment (e.g. a V3 vs V2 router, or a router on another chain) will fail.","commonSituations":"Configuring `router_addresses` for one chain but approving on another; using a Uniswap V2 router with a V3-only allowlist (or vice versa); hardcoding a router from a mainnet deployment while running against a testnet; adding a new swap venue to the strategy without updating the allowlist; typos or stale checksummed hex in the config.","solutions":["Add the router address actually being used to the `router_addresses` allowlist in the client/blockchain adapter configuration, then restart the client.","Verify the router address matches the deployment for the target chain (e.g. Uniswap V3 SwapRouter02 vs SwapRouter) and that you are connected to the intended chain.","If the router is unexpected, audit where it came from: a swap plan or venue config may be passing a stale or wrong router to approve(); fix the caller to use a router from the registry/config instead."],"exampleFix":"// before: router hardcoded, not in config\nlet router: Address = \"0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45\".parse()?;\nclient.approve(token, amount, router).await?;\n\n// after: reuse a router address from the configured allowlist\nlet router = client.config.router_addresses.first().copied()\n    .context(\"no routers configured\")?;\nclient.approve(token, amount, router).await?;","handlingStrategy":"validation","validationCode":"let router: Address = router_addr.parse()?;\nif !client.router_addresses.contains(&router) {\n    anyhow::bail!(\"router {router} is not allowlisted; update router_addresses config\");\n}\n// safe to call client.approve(token, amount, router).await?","typeGuard":"fn is_allowlisted_router(router: Address, allowlist: &[Address]) -> bool {\n    allowlist.contains(&router)\n}","tryCatchPattern":"match client.approve(token, amount, router).await {\n    Ok(tx_hash) => info!(%tx_hash, \"approval confirmed\"),\n    Err(e) if e.to_string().contains(\"router_addresses\") => {\n        error!(\"router not in allowlist — fix config, do not retry\");\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Keep a single source of truth for router addresses in config and never hardcode them in strategy code.","Assert at startup that every venue/strategy router is present in router_addresses.","Pin per-chain router addresses and validate them against the connected chain ID."],"tags":["config","security","blockchain","allowlist","erc20-approval"],"backgroundTag":"invalid-config-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}