nautechsystems/nautilus_trader · error
Pool fee is invalid
Error message
Pool fee is invalid
What it means
Each pool entry in the deployment manifest must carry a nonzero swap fee that does not exceed 1,000,000 (fee tier in pool-fee units, e.g. Uniswap-style granularity). The library enforces this because a zero fee is nonsensical for a live pool and an out-of-range value indicates a corrupted or fabricated pool record.
Source
Thrown at crates/adapters/blockchain/src/rpc/verification.rs:1383
);
}
for pool in &manifest.pools {
for address in [
&pool.address,
&pool.token0,
&pool.token1,
&pool.factory,
&pool.quote_contract,
] {
let address = Address::from_str(address)
.map_err(|_| anyhow::anyhow!("Pool manifest address is invalid"))?;
anyhow::ensure!(
contract_addresses.contains(&address),
"Pool manifest references an unpinned contract"
);
}
anyhow::ensure!(
pool.fee != 0 && pool.fee <= 1_000_000,
"Pool fee is invalid"
);
}
let mut call_edges = HashSet::new();
for edge in &manifest.call_edges {
anyhow::ensure!(
matches!(
edge.purpose.as_str(),
"wrap" | "approve" | "swap_sell" | "swap_buy"
) && matches!(
edge.call_type.as_str(),
"call" | "staticcall" | "delegatecall" | "callcode"
),
"Deployment manifest call edge is invalid"
);
let caller = Address::from_str(&edge.caller)
.map_err(|_| anyhow::anyhow!("Call edge address is invalid"))?;View on GitHub (pinned to 18893faf8b)
Solutions
- Set pool.fee to the pool's real fee tier (e.g. 500, 3000, 10000 for Uniswap v3-style tiers)
- If the fee was expressed in percent or basis points, convert it to the manifest's fee units before writing it
- Remove placeholder pool entries that were never fully populated
- Regenerate the manifest from the source of truth (chain data / generator) instead of editing by hand
Example fix
// before
{"address": "0xabc...", "fee": 0}
// after
{"address": "0xabc...", "fee": 3000} Defensive patterns
Strategy: validation
Validate before calling
// Pre-check fee before building the manifest
if !(pool.fee > 0 && pool.fee <= 1_000_000) {
return Err(format!("pool {} fee {} out of range (1..=1_000_000)", pool.address, pool.fee));
} Type guard
fn is_valid_fee(fee: u64) -> bool { fee != 0 && fee <= 1_000_000 } Try / catch
match validate_deployment_manifest(&manifest) {
Err(e) if e.to_string().contains("Pool fee is invalid") => {
eprintln!("Set pool.fee to a nonzero value <= 1_000_000 (fee-tier units)");
}
Err(e) => return Err(e),
Ok(()) => {}
} Prevention
- Copy fee values directly from chain data (e.g. Uniswap fee tiers 500/3000/10000) rather than converting by hand
- Add a unit test that every fixture pool fee is in 1..=1_000_000
- Reject fee=0 placeholder entries at manifest authoring time
When it happens
Trigger: Validating a manifest whose pool.fee equals 0 or is greater than 1,000,000; the check runs after the pool address/pinning validation passes.
Common situations: Leaving fee at its default 0 when hand-authoring a pool entry; copying a fee from a venue that uses a different unit (e.g. percent 0.3 instead of 3000); a typo like 10000000 instead of 1000000.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Pool manifest references an unpinned contract
- Deployment manifest is missing a swap call graph
- 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/479db40853318bab.
Report an issue: GitHub.