nautechsystems/nautilus_trader · error

No pool profiler for

Error message

No pool profiler for {instrument_id}; an active data subscription is required to quote the swap

What it means

Swaps are quoted using an in-memory pool profiler built from live pool-event data. If the cache holds no profiler for the instrument, there is no active data subscription feeding pool state, and the adapter refuses to quote the swap rather than using stale or missing liquidity data.

Solutions

  1. Start an active data subscription for the instrument/pool before submitting swap orders
  2. Verify the instrument_id in the order exactly matches the subscribed instrument (network/address/tick-size)
  3. Wait for profiler warm-up after connect until pool_profiler appears in the cache
  4. Check that the data client for this venue/pool is enabled in configuration

Example fix

// before: submitting immediately after connect
await client.connect();
client.submit_order(swap_order);
// after: ensure subscription and profiler exist first
await client.subscribe_pool(instrument_id);
while client.cache().pool_profiler(instrument_id).is_none() { sleep(100ms); }
client.submit_order(swap_order);
Defensive patterns

Strategy: validation

Validate before calling

if client.cache().pool_profiler(&instrument_id).is_none() {
    return Err(format!("no pool profiler for {instrument_id}; subscribe before trading"));
}

Type guard

fn profiler_ready(cache: &Cache, id: &InstrumentId) -> bool {
    cache.pool_profiler(id).map(|p| p.is_initialized && p.last_processed_event.is_some()).unwrap_or(false)
}

Try / catch

match client.submit_order(order).await {
    Err(e) if e.to_string().contains("No pool profiler") => {
        client.subscribe_pool(instrument_id).await?;
        wait_for_profiler(&instrument_id).await?;
        client.submit_order(order).await
    }
    r => r,
}

Prevention

When it happens

Trigger: `submit_order()` -> `prepare_swap()` calls `cache().pool_profiler(&instrument_id)` and gets None because no data subscription for that pool is active.

Common situations: Ordering on a DEX instrument whose market-data stream was never subscribed or was unsubscribed; cache restarted and profiler not yet rebuilt; instrument_id mismatch between the trading config and the subscription.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/05f2b50a283a8cd7. Report an issue: GitHub.

Appendix: source

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

            anyhow::ensure!(
                ceiling.spend_token_decimals == quote_token.decimals,
                "Quote spend limit for token {} uses {} decimals, expected pool quote-token decimals {}",
                ceiling.spend_token,
                ceiling.spend_token_decimals,
                quote_token.decimals
            );
            Some(ceiling)
        } else {
            None
        };

        let profiler = self
            .core
            .cache()
            .pool_profiler(&instrument_id)
            .cloned()
            .ok_or_else(|| {
                anyhow::anyhow!(
                    "No pool profiler for {instrument_id}; an active data subscription is required to quote the swap"
                )
            })?;

        if !profiler.is_initialized {
            anyhow::bail!("Pool profiler for {instrument_id} is not initialized");
        }
        let profiler_position = profiler.last_processed_event.clone().ok_or_else(|| {
            anyhow::anyhow!("Pool profiler for {instrument_id} has processed no events")
        })?;

        let zero_for_one = token_in == pool.token0.address;
        let (amount_in, quoted_amount_out) = match order.order_side() {
            OrderSide::Sell => {
                let quote = profiler
                    .swap_exact_in(base_amount, zero_for_one, None)
                    .map_err(|e| anyhow::anyhow!("Swap quote failed for {instrument_id}: {e}"))?;
                let amount_filled = if zero_for_one {

View on GitHub (pinned to 18893faf8b)