nautechsystems/nautilus_trader · error · anyhow::Error
Pool {instrument_id} has no fee tier
Error message
Pool {instrument_id} has no fee tier What it means
While preparing a swap order, the resolved pool for the instrument has fee = None. Uniswap V3 pools always have a fee tier (in hundredths of a bip, e.g. 500 = 0.05%), and the swap calldata cannot be built without it, so the order is rejected. The None comes from the pool metadata in the cache (fee is Option<u32>, populated from pool registry/PoolCreated data).
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:1178
);
}
if order.order_side() != OrderSide::Sell {
anyhow::bail!(
"Unsupported order side {}; only Sell is supported",
order.order_side()
);
}
if order.is_quote_quantity() {
anyhow::bail!(
"Quote-denominated quantities are not supported; quantity must be denominated in the base token"
);
}
let fee = pool
.fee
.ok_or_else(|| anyhow::anyhow!("Pool {instrument_id} has no fee tier"))?;
let fee = U24::try_from(fee)
.map_err(|_| anyhow::anyhow!("Pool {instrument_id} fee {fee} exceeds uint24"))?;
let base_token = pool.get_base_token();
let quote_token = pool.get_quote_token();
let quote_currency = Currency::new_checked(
"e_token.symbol,
quote_token.decimals,
0,
"e_token.name,
CurrencyType::Crypto,
)?;
if !self
.transaction_limits
.allowed_token_pairs
.contains(&(base_token.address, quote_token.address))
{View on GitHub (pinned to 2114cf6f76)
Solutions
- Check the pool definition for the instrument in the adapter configuration and confirm it includes the fee tier
- Verify on-chain that the pool address is a real Uniswap V3 pool and read its fee() (immutable) to get the correct value
- Restart/refresh the data subscription so the pool cache repopulates with complete metadata before submitting orders
- Use a fee value in hundredths of a bip (500 for 0.05%, 3000 for 0.30%, 10000 for 1%)
Example fix
# before: pool registered without a fee tier Pool(instrument_id=WETH_USDC, address="0x...", fee=None) # after: fee tier present in Uniswap V3 units (0.05%) Pool(instrument_id=WETH_USDC, address="0x...", fee=500)
Defensive patterns
Strategy: validation
Validate before calling
# Before trading, confirm every configured pool exposes a fee tier
for instrument_id in instruments_to_trade:
pool = get_pool_definition(instrument_id)
assert pool.fee is not None, f'{instrument_id} has no fee tier configured'
assert 0 < pool.fee <= 0xFFFFFF, f'{instrument_id} fee out of Uniswap V3 range' Prevention
- Always configure pools with their Uniswap V3 fee tier (500/3000/10000 etc.), read from the pool contract when in doubt
- Prefer resolving pools via the adapter's own discovery/subscription flow instead of hand-written metadata
When it happens
Trigger: submit_order on an instrument whose cached pool definition was registered without a fee tier: instrument/pool configuration missing the fee field, or a PoolCreated event observed without fee data before full pool initialization.
Common situations: Adding a new pool to the adapter config and forgetting the fee tier; pointing the instrument provider at a custom factory whose PoolCreated event omits fee; race at startup where the pool is resolved before its metadata is complete.
Related errors
- Pool {instrument_id} fee {fee} exceeds uint24
- No deployed bytecode at {description} address {address}
- No pool profiler for {instrument_id}; an active data subscri
- Pool profiler for {instrument_id} has processed no events
- Swap quote failed for {instrument_id}: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21).
Data as JSON: /api/errors/b2e74b7b0eeeeb9e.
Report an issue: GitHub.