{"record":{"id":"e1cc7a8e9a047665","repo":"nautechsystems/nautilus_trader","slug":"no-quote-spend-limits-entry-for-buy-token-pair","errorCode":null,"errorMessage":"No `quote_spend_limits` entry for BUY token pair {token_in} -> {token_out}","messagePattern":"No `quote_spend_limits` entry for BUY token pair (.+?) -> (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":1631,"sourceCode":"                anyhow::anyhow!(\"slippage_bps parameter {value} exceeds the u32 range\")\n            })?,\n            None => self.transaction_limits.slippage_bps,\n        };\n\n        if slippage_bps > self.transaction_limits.max_slippage_bps {\n            anyhow::bail!(\n                \"Slippage {slippage_bps} bps exceeds the configured `max_slippage_bps` {}\",\n                self.transaction_limits.max_slippage_bps\n            );\n        }\n\n        let quote_spend_ceiling = if order.order_side() == OrderSide::Buy {\n            let ceiling = self\n                .transaction_limits\n                .quote_spend_limits\n                .get(&(token_in, token_out))\n                .ok_or_else(|| {\n                    anyhow::anyhow!(\n                        \"No `quote_spend_limits` entry for BUY token pair {token_in} -> {token_out}\"\n                    )\n                })?;\n            anyhow::ensure!(\n                ceiling.spend_token == quote_token.address,\n                \"Quote spend limit for {token_in} -> {token_out} is denominated in {}, expected quote token {}\",\n                ceiling.spend_token,\n                quote_token.address\n            );\n            anyhow::ensure!(\n                ceiling.spend_token_decimals == quote_token.decimals,\n                \"Quote spend limit for token {} uses {} decimals, expected pool quote-token decimals {}\",\n                ceiling.spend_token,\n                ceiling.spend_token_decimals,\n                quote_token.decimals\n            );\n            Some(ceiling)\n        } else {","sourceCodeStart":1613,"sourceCodeEnd":1649,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L1613-L1649","documentation":"BUY swaps spend the quote token, and the adapter enforces a per-pair quote-spend ceiling. `transaction_limits.quote_spend_limits` has no entry for the (token_in, token_out) pair, so a BUY order cannot be cost-capped and is rejected.","triggerScenarios":"`submit_order()` -> `prepare_swap()` with `order.order_side() == OrderSide::Buy` and no `quote_spend_limits` map entry keyed by the exact (token_in, token_out) address pair.","commonSituations":"Configured spend limits for the inverse direction only; token address casing/checksum mismatch as map key; new market added to trading config without adding its spend-limit entry.","solutions":["Add a `quote_spend_limits` entry keyed by the exact (token_in, token_out) pair used by the order","Verify the token addresses in the map match the pool's token addresses exactly (same checksum/case)","Either configure limits for all BUY pairs you trade, or route the order as a SELL-side pair if appropriate"],"exampleFix":"// before\nquote_spend_limits: {} \n// after\nquote_spend_limits: {\n  (usdc_addr, weth_addr): QuoteSpendCeiling { spend_token: usdc_addr, spend_token_decimals: 6, max_spend: ... },\n}","handlingStrategy":"validation","validationCode":"if order.order_side() == OrderSide::Buy\n    && !transaction_limits.quote_spend_limits.contains_key(&(token_in, token_out)) {\n    return Err(format!(\"add quote_spend_limits entry for {token_in} -> {token_out}\"));\n}","typeGuard":"fn has_spend_limit(limits: &HashMap<(Address, Address), Ceiling>, tin: Address, tout: Address) -> bool {\n    limits.contains_key(&(tin, tout))\n}","tryCatchPattern":"match client.submit_order(buy_order).await {\n    Err(e) if e.to_string().contains(\"No `quote_spend_limits` entry\") => {\n        // block trading this pair until an operator configures a ceiling\n        disable_pair(token_in, token_out);\n        Err(e)\n    }\n    r => r,\n}","preventionTips":["Maintain spend-limit entries for every BUY pair enabled in trading config","Key limits on exact token addresses used by pools (consistent checksum/case)","Add a config validation pass at startup that cross-checks tradable pairs against quote_spend_limits"],"tags":["configuration","risk-limits","swap","buy-order"],"backgroundTag":"missing-required-config-field","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}