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
- 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().
- 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).
- 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
- Whenever a new instrument is registered, add its token pair to allowed_token_pairs in the same change.
- Approve only tokens the strategy actively trades; prefer targeted (non-unlimited) approvals.
- Use zero-amount approvals for revocation since they bypass the token-pairs check.
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
- Router {router} is not in the configured `router_addresses`
- Verified chain ID does not match the transaction chain
- DEX {dex_id} is not registered in the data client
- `router_addresses` must contain at least one router address
- Quote spend limit for {token_in} -> {token_out} is denominat
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b4a3073a2cdfd6cf.
Report an issue: GitHub.