nautechsystems/nautilus_trader · error
Quote spend limit for token {} uses {} decimals, expected po
Error message
Quote spend limit for token {} uses {} decimals, expected pool quote-token decimals {} What it means
The quote-spend ceiling's decimal precision must match the pool quote token's decimals so the cap amount is interpreted in the same units as the spend. A mismatch would silently under/over-cap, so it is rejected.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:1641
}
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 = self
.core
.cache()
.pool_profiler(&instrument_id)
.cloned()
.ok_or_else(|| {
anyhow::anyhow!(View on GitHub (pinned to 18893faf8b)
Solutions
- Set `spend_token_decimals` to the pool quote token's actual decimals in the spend-limit config
- Re-derive decimals from the token contract/metadata instead of hardcoding
- If the address matched but decimals differ, suspect a proxy/scam token — verify the token before trading
Example fix
// before spend_token_decimals: 18 // USDC // after spend_token_decimals: 6
Defensive patterns
Strategy: validation
Validate before calling
let ceiling = limits.get(&(token_in, token_out)).unwrap();
if ceiling.spend_token_decimals != quote_token.decimals {
return Err(format!("decimals {} != {}", ceiling.spend_token_decimals, quote_token.decimals));
} Type guard
fn decimals_match(ceiling: &Ceiling, quote: &Token) -> bool {
ceiling.spend_token_decimals == quote.decimals
} Try / catch
match client.submit_order(buy_order).await {
Err(e) if e.to_string().contains("decimals") => {
// refetch token metadata and regenerate the ceiling, then retry
regenerate_spend_limit("e_token)?;
client.submit_order(buy_order).await
}
r => r,
} Prevention
- Derive decimals from token contract metadata instead of hardcoding
- Remember stablecoins often use 6 decimals, not 18
- Cross-check ceiling decimals against quote_token.decimals at config load
When it happens
Trigger: `prepare_swap()` on a BUY order where `ceiling.spend_token_decimals != quote_token.decimals` (token matched by address but decimals configured wrong, or ceiling reused across wrapped/underlying variants).
Common situations: Configured 18 decimals for USDC/USDT (6); ceiling entry copied from a wrapped token (WETH 18) to base token; upstream token contract changed decimals metadata.
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_in} -> {token_out} is denominat
- 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/93ac09ee2ea7e9c9.
Report an issue: GitHub.