nautechsystems/nautilus_trader · error
Quote spend limit for {token_in} -> {token_out} is denominat
Error message
Quote spend limit for {token_in} -> {token_out} is denominated in {}, expected quote token {} What it means
For BUY swaps the configured quote-spend ceiling must be denominated in the pool's actual quote token. If `ceiling.spend_token` differs from `quote_token.address`, the cap would limit the wrong asset, so the swap is rejected.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:1635
if slippage_bps > self.transaction_limits.max_slippage_bps {
anyhow::bail!(
"Slippage {slippage_bps} bps exceeds the configured `max_slippage_bps` {}",
self.transaction_limits.max_slippage_bps
);
}
let quote_spend_ceiling = if order.order_side() == OrderSide::Buy {
let ceiling = self
.transaction_limits
.quote_spend_limits
.get(&(token_in, token_out))
.ok_or_else(|| {
anyhow::anyhow!(
"No `quote_spend_limits` entry for BUY token pair {token_in} -> {token_out}"
)
})?;
anyhow::ensure!(
ceiling.spend_token == quote_token.address,
"Quote spend limit for {token_in} -> {token_out} is denominated in {}, expected quote token {}",
ceiling.spend_token,
quote_token.address
);
anyhow::ensure!(
ceiling.spend_token_decimals == quote_token.decimals,
"Quote spend limit for token {} uses {} decimals, expected pool quote-token decimals {}",
ceiling.spend_token,
ceiling.spend_token_decimals,
quote_token.decimals
);
Some(ceiling)
} else {
None
};
let profiler = selfView on GitHub (pinned to 18893faf8b)
Solutions
- Correct the ceiling's `spend_token` to the pool's actual quote-token address
- Fix the (token_in, token_out) key so the ceiling matches the pair actually being swapped
- Recompute the spend limit if the real quote token has different decimals/value
Example fix
// before spend_token: USDT_ADDRESS // pool quote is USDC // after spend_token: USDC_ADDRESS // matches quote_token.address
Defensive patterns
Strategy: validation
Validate before calling
let ceiling = limits.get(&(token_in, token_out)).ok_or("missing ceiling")?;
if ceiling.spend_token != quote_token.address {
return Err("ceiling denominated in wrong token".into());
} Type guard
fn ceiling_matches_quote(ceiling: &Ceiling, quote: &Token) -> bool {
ceiling.spend_token == quote.address
} Try / catch
match client.submit_order(buy_order).await {
Err(e) if e.to_string().contains("denominated in") => {
reload_spend_limits_for_pair(token_in, token_out)?;
client.submit_order(buy_order).await
}
r => r,
} Prevention
- Generate spend-limit entries from pool metadata so spend_token always matches the real quote token
- Verify pool token ordering (token0/token1 vs base/quote) when configuring limits
- Don't copy ceiling entries between markets with different quote tokens
When it happens
Trigger: `prepare_swap()` on a BUY order where the `quote_spend_limits` entry's `spend_token` address does not equal the pool's quote-token address.
Common situations: Pool where the configured 'quote' differs from the actual pool quote token (e.g. USDT vs USDC pair swapped); copy-pasted limit entry between markets; token address checksum/case mismatch.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- No `quote_spend_limits` entry for BUY token pair {token_in}
- Quote spend limit for token {} uses {} decimals, expected po
- No deployed bytecode at {description} address {address}
- Router allowance {allowance} is below the swap amount {} for
- Input token {} balance {balance} is below the swap amount {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/fef3d59c39ad4e5e.
Report an issue: GitHub.