nautechsystems/nautilus_trader · error

Quote spend limit pair {token_in} -> {token_out} is not in t

Error message

Quote spend limit pair {token_in} -> {token_out} is not in the `allowed_token_pairs` allowlist

What it means

Each quote_spend_limits entry's (token_in, token_out) pair must appear in the allowed_token_pairs allowlist parsed from config. The library throws this when a spend limit references a pair that is not whitelisted, because spend limits only constrain pairs the client is allowed to trade.

Source

Thrown at crates/adapters/blockchain/src/execution/client.rs:297

        };

        let mut parsed_pairs = HashSet::with_capacity(allowed_token_pairs.len());
        for (token_in, token_out) in allowed_token_pairs {
            parsed_pairs.insert((
                validate_address(token_in.as_str())?,
                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
                );
            }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Add the limit's (token_in, token_out) pair to config.allowed_token_pairs
  2. Check the pair ordering matches between allowed_token_pairs and quote_spend_limits
  3. Verify the addresses in both lists refer to the same contracts on the same chain

Example fix

// before
allowed_token_pairs = []
quote_spend_limits = [{ token_in = "0xA", token_out = "0xB", spend_token = "0xA", max_amount = "1000" }]
// after
allowed_token_pairs = [["0xA", "0xB"]]
quote_spend_limits = [{ token_in = "0xA", token_out = "0xB", spend_token = "0xA", max_amount = "1000" }]
Defensive patterns

Strategy: validation

Validate before calling

let allowed: HashSet<(String, String)> = cfg.allowed_token_pairs.iter().cloned().collect();
for l in &cfg.quote_spend_limits {
    if !allowed.contains(&(l.token_in.clone(), l.token_out.clone())) {
        return Err(format!("spend limit pair {} -> {} not in allowed_token_pairs", l.token_in, l.token_out));
    }
}

Prevention

When it happens

Trigger: Calling new/transaction_limits with config.quote_spend_limits containing a limit whose (token_in, token_out) after address validation does not match any entry in config.allowed_token_pairs (case/representation differences in raw strings are normalized by validate_address, so the mismatch must be a genuinely different pair).

Common situations: Operator adds a spend limit but forgets to add the pair to allowed_token_pairs; copy-paste of token addresses between chains; swapped token_in/token_out order in one of the two lists.

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/0a38098ce2df3dbf. Report an issue: GitHub.