nautechsystems/nautilus_trader · error
Pool fee should be initialized
Error message
Pool fee should be initialized
What it means
simulate_swap_through_ticks reads self.pool.fee to get the fee tier used in swap math; if the fee was never initialized on the pool, expect panics. The fee tier is essential for computing fees accrued within each swap step, so simulation cannot proceed without it.
Source
Thrown at crates/model/src/defi/pool_analysis/profiler.rs:494
/// # Errors
///
/// Returns error if:
/// - Pool fee is not configured
/// - Fee growth arithmetic overflows when scaling by liquidity
/// - Swap step calculations fail
///
/// # Panics
///
/// Panics if the pool fee has not been initialized.
pub fn simulate_swap_through_ticks(
&self,
amount_specified: I256,
zero_for_one: bool,
sqrt_price_limit_x96: U160,
traverse_empty_ranges: bool,
) -> anyhow::Result<SwapQuote> {
let exact_input = amount_specified.is_positive();
let fee_tier = self.pool.fee.expect("Pool fee should be initialized");
let mut current_sqrt_price = self.state.price_sqrt_ratio_x96;
let mut current_tick = self.state.current_tick;
let mut current_active_liquidity = self.tick_map.liquidity;
let mut amount_specified_remaining = amount_specified;
let mut amount_calculated = I256::ZERO;
let mut protocol_fee = U256::ZERO;
let mut lp_fee = U256::ZERO;
let mut crossed_ticks = Vec::new();
// Swapping cache variables
let fee_protocol = self.state.uniswap_v3_fee_protocol(zero_for_one);
let fee_protocol_basis_points = self.state.fee_protocol_basis_points(zero_for_one);
// Track current fee growth during swap
let mut current_fee_growth_global = if zero_for_one {
self.state.fee_growth_global_0
} else {View on GitHub (pinned to 18893faf8b)
Solutions
- Initialize pool.fee from the factory/pool fee() call or the mint/create event before running swap simulation.
- Call check_if_initialized (or an equivalent guard) and return an error instead of proceeding when fee is None.
- Validate pool completeness (fee, tick_spacing) at profiler construction so uninitialized pools fail early with a clear error.
Example fix
// before
let fee_tier = self.pool.fee.expect("Pool fee should be initialized");
// after
let fee_tier = self.pool.fee.ok_or_else(|| anyhow::anyhow!(
"pool {} fee not initialized; cannot simulate swap", self.pool.address
))?; Defensive patterns
Strategy: validation
Validate before calling
if self.pool.fee.is_none() { return Err(anyhow::anyhow!("pool fee not initialized")); } Type guard
fn fee_tier(pool: &SharedPool) -> Option<u32> { pool.fee } Try / catch
match self.pool.fee { Some(f) => simulate_with(f), None => Err(anyhow!("fee missing")), } Prevention
- Guarantee pool.fee is set by the topology loader before simulation.
- Run a pre-flight check (fee and tick_spacing present) on every profiler used for quoting.
- Persist fee with snapshots.
When it happens
Trigger: Calling simulate_swap_through_ticks (directly or via process_swap, execute_swap, quote_swap) on a profiler whose pool.fee is None — i.e. the pool's fee rate was not captured during topology initialization.
Common situations: Quoting swaps against a pool seeded from partial data; pools discovered via RPC where the fee() call failed silently; test fixtures building SharedPool without fee.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Pool tick spacing must be set
- Latency model should be initialized
- Execution client should be initialized
- Native currency not specified for chain {}
- Must have the `chain` field set
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ab9fdb65670a7b94.
Report an issue: GitHub.