nautechsystems/nautilus_trader · error

Token {token} is not an input token in the configured `allow

Error message

Token {token} is not an input token in the configured `allowed_token_pairs`

What it means

approve() only grants an ERC-20 allowance to a router if the token appears as an input token (`token_in`) in at least one pair of the configured `allowed_token_pairs` transaction limits, and the requested amount is nonzero. This confines token approvals to tokens the strategy is actually configured to trade, so a compromised or buggy code path cannot approve arbitrary tokens. A nonzero approval for any other token bails immediately.

Source

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

    /// finality, leaves the in-flight slot occupied.
    pub async fn approve(
        &mut self,
        token: Address,
        amount: U256,
        router: Address,
    ) -> anyhow::Result<B256> {
        if !self.router_addresses.contains(&router) {
            anyhow::bail!("Router {router} is not in the configured `router_addresses` allowlist");
        }

        if !amount.is_zero()
            && !self
                .transaction_limits
                .allowed_token_pairs
                .iter()
                .any(|(token_in, _)| *token_in == token)
        {
            anyhow::bail!(
                "Token {token} is not an input token in the configured `allowed_token_pairs`"
            );
        }

        self.ensure_transaction_ready(TransactionPurpose::Approve)?;

        let approval_amount = if amount.is_zero() {
            U256::ZERO
        } else if self.config.unlimited_approval {
            U256::MAX
        } else {
            amount
        };
        let calldata = ERC20::approveCall {
            spender: router,
            amount: approval_amount,
        }
        .abi_encode();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Add the token as the `token_in` side of an entry in the `allowed_token_pairs` section of the transaction_limits configuration, then re-run approve().
  2. Confirm the token address matches the one used in the configured pairs (Address equality is exact; check the token's actual on-chain address for that chain, not another chain's deployment).
  3. If you only intended to revoke an allowance, pass `U256::ZERO` as the amount; the token-pairs check is skipped for zero approvals.

Example fix

// before: approving a token not listed in limits
client.approve(new_token, U256::from(1_000_000), router).await?;

// after: add (new_token, quote_token) to allowed_token_pairs in config first,
// then approve
debug_assert!(limits.allowed_token_pairs.iter().any(|(t_in, _)| *t_in == new_token));
client.approve(new_token, U256::from(1_000_000), router).await?;
Defensive patterns

Strategy: validation

Validate before calling

if !amount.is_zero()
    && !limits.allowed_token_pairs.iter().any(|(token_in, _)| *token_in == token)
{
    anyhow::bail!("token {token} not an allowed input; extend allowed_token_pairs");
}
// safe to call client.approve(token, amount, router).await?

Type guard

fn is_allowed_input(token: Address, limits: &TransactionLimits) -> bool {
    limits.allowed_token_pairs.iter().any(|(t_in, _)| *t_in == token)
}

Try / catch

if let Err(e) = client.approve(token, amount, router).await {
    if e.to_string().contains("allowed_token_pairs") {
        warn!("token missing from transaction_limits; skipping approval");
        return Ok(()); // or surface for config review
    }
    return Err(e.into());
}

Prevention

When it happens

Trigger: Calling `client.approve(token, amount, router)` with a nonzero `amount` where no entry in `transaction_limits.allowed_token_pairs` has that token as the first element (`token_in`). Note the guard is skipped entirely when `amount` is zero (revocation approvals are always allowed).

Common situations: Strategy starts trading a newly added instrument (e.g. a new USDC/WETH-style pair) without extending `allowed_token_pairs`; a typo in the token address in the limits config; approving the quote token when only the base token was configured as an input; approving a settlement/intermediate token that is not directly an input.

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/b4a3073a2cdfd6cf. Report an issue: GitHub.