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 {spend_token}; `spend_token` must match `token_in` What it means
Quote spend limits must be denominated in the input token: spend_token must equal token_in. The library throws this when a limit's spend_token address differs from its token_in, since the client caps spend of the token being sold into the swap.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:303
validate_address(token_out.as_str())?,
));
}
let quote_spend_limits = config.quote_spend_limits.as_deref().unwrap_or_default();
let mut parsed_quote_spend_limits = HashMap::with_capacity(quote_spend_limits.len());
for limit in quote_spend_limits {
let token_in = validate_address(limit.token_in.as_str())?;
let token_out = validate_address(limit.token_out.as_str())?;
let spend_token = validate_address(limit.spend_token.as_str())?;
if !parsed_pairs.contains(&(token_in, token_out)) {
anyhow::bail!(
"Quote spend limit pair {token_in} -> {token_out} is not in the `allowed_token_pairs` allowlist"
);
}
if spend_token != token_in {
anyhow::bail!(
"Quote spend limit for {token_in} -> {token_out} is denominated in {spend_token}; `spend_token` must match `token_in`"
);
}
if limit.max_amount.is_empty()
|| !limit.max_amount.bytes().all(|byte| byte.is_ascii_digit())
{
anyhow::bail!(
"Quote spend limit `max_amount` '{}' must be a base-10 unsigned integer string",
limit.max_amount
);
}
let max_amount = U256::from_str(&limit.max_amount).map_err(|_| {
anyhow::anyhow!(
"Quote spend limit `max_amount` '{}' exceeds the U256 range",
limit.max_amount
)
})?;View on GitHub (pinned to 18893faf8b)
Solutions
- Set spend_token in the limit entry to the same address as token_in
- Re-check the limit's semantics: it caps the amount of token_in spent per quote
- Fix any address copy-paste mix-ups between fields
Example fix
// before
{ token_in = "0xWETH", token_out = "0xUSDC", spend_token = "0xUSDC", max_amount = "1000000000" }
// after
{ token_in = "0xWETH", token_out = "0xUSDC", spend_token = "0xWETH", max_amount = "1000000000000000000" } Defensive patterns
Strategy: validation
Validate before calling
for l in &cfg.quote_spend_limits {
if !l.spend_token.eq_ignore_ascii_case(&l.token_in) {
return Err(format!("spend_token {} must equal token_in {}", l.spend_token, l.token_in));
}
} Prevention
- Remember the limit caps the input token spent, not the output received
- Build limits via a helper that derives spend_token from token_in automatically
- Add a config-lint unit test asserting spend_token == token_in for all entries
When it happens
Trigger: Calling new/transaction_limits with a config.quote_spend_limits entry where validate_address(limit.spend_token) != validate_address(limit.token_in).
Common situations: Setting spend_token to the output token (USDC) instead of the input token (WETH); copy-paste of addresses in the wrong field; assuming the limit applies to proceeds rather than spend.
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
- `router_addresses` must contain at least one router address
- Independent Blockchain execution verification is required
- Verification chain anchor ID does not match the configured c
- Verification chain anchor name does not match the configured
- Allowed token pair {token_in} -> {token_out} is not fully pi
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/a7e6cb97a1e26d6a.
Report an issue: GitHub.