nautechsystems/nautilus_trader · error

Duplicate quote spend limit for token pair {token_in} -> {to

Error message

Duplicate quote spend limit for token pair {token_in} -> {token_out}

What it means

Each (token_in, token_out) pair may have at most one quote spend limit; the parsed map insert returns Some on a duplicate key. The library throws this to keep the limits map unambiguous rather than silently overwriting a ceiling.

Source

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

                );
            }
            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
                )
            })?;
            let ceiling = QuoteSpendCeiling {
                spend_token,
                spend_token_decimals: limit.spend_token_decimals,
                max_amount,
            };

            if parsed_quote_spend_limits
                .insert((token_in, token_out), ceiling)
                .is_some()
            {
                anyhow::bail!(
                    "Duplicate quote spend limit for token pair {token_in} -> {token_out}"
                );
            }
        }

        if slippage_bps > max_slippage_bps {
            anyhow::bail!(
                "`slippage_bps` {slippage_bps} exceeds `max_slippage_bps` {max_slippage_bps}"
            );
        }

        if max_slippage_bps >= BPS_DENOMINATOR {
            anyhow::bail!("`max_slippage_bps` {max_slippage_bps} must be below {BPS_DENOMINATOR}");
        }

        if !(1..=4_095).contains(&max_quote_age_blocks) {
            anyhow::bail!("`max_quote_age_blocks` must be in 1..=4095");
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Remove the duplicate quote_spend_limits entry, keeping the stricter (smaller) max_amount if unsure
  2. Deduplicate the list before writing config (unique by token pair)
  3. Check config merge/diff tooling for accidental duplication

Example fix

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

Strategy: validation

Validate before calling

let mut seen = HashSet::new();
for l in &cfg.quote_spend_limits {
    if !seen.insert((l.token_in.clone(), l.token_out.clone())) {
        return Err(format!("duplicate quote spend limit for {} -> {}", l.token_in, l.token_out));
    }
}

Prevention

When it happens

Trigger: Calling new/transaction_limits with config.quote_spend_limits containing two entries whose validated (token_in, token_out) pairs are equal (e.g. same addresses differing only in checksum casing before validation).

Common situations: Merged config files appending duplicate limit blocks; same pair listed twice with different max_amounts by mistake; duplicated entries after YAML/TOML merge tooling.

Related errors


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