nautechsystems/nautilus_trader · error · anyhow::Error

Pool profiler for {instrument_id} has processed no events

Error message

Pool profiler for {instrument_id} has processed no events

What it means

The pool profiler for the instrument exists and is initialized, but last_processed_event is None, meaning the profiler has not yet ingested any pool events (Swap/Mint/Burn/etc.) and therefore has no liquidity state to quote against. The block number of the last processed event is also persisted into the swap plan, so quoting must stop here.

Source

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

            .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_block = profiler
            .last_processed_event
            .as_ref()
            .map(|position| position.number)
            .ok_or_else(|| {
                anyhow::anyhow!("Pool profiler for {instrument_id} has processed no events")
            })?;

        let zero_for_one = base_token.address == pool.token0.address;
        let quote = profiler
            .swap_exact_in(amount_in, zero_for_one, None)
            .map_err(|e| anyhow::anyhow!("Swap quote failed for {instrument_id}: {e}"))?;

        let amount_filled = if zero_for_one {
            quote.amount0
        } else {
            quote.amount1
        };

        if amount_filled != I256::from(amount_in) {
            anyhow::bail!(
                "Local quote for {instrument_id} filled {amount_filled} of the {amount_in} order amount; pool liquidity cannot fill the order"
            );
        }

View on GitHub (pinned to 2114cf6f76)

Solutions

  1. Wait for the first pool event/quote for the instrument before submitting (drive submission from on_quote/on_deltas callbacks)
  2. Verify the subscription start block/window actually covers pool activity; widen it if the pool has been quiet
  3. For freshly created pools, wait until initial liquidity events have been ingested

Example fix

# before
self.subscribe_quote(instrument_id)
self.submit_order(order)  # immediately: has processed no events

# after: gate on first data event
self.subscribe_quote(instrument_id)

def on_quote_tick(self, tick):
    if tick.instrument_id == self.instrument_id and not self._traded:
        self._traded = True
        self.submit_order(order)
Defensive patterns

Strategy: retry

Validate before calling

# Only trade once the profiler has ingested events (first quote observed)
assert self.cache.quote_tick(instrument_id) is not None, (
    f'profiler for {instrument_id} has processed no events yet; wait for the first quote'
)

Type guard

def pool_is_quotable(cache, instrument_id) -> bool:
    """A cached quote implies the pool profiler processed at least one event."""
    return cache.quote_tick(instrument_id) is not None

Prevention

When it happens

Trigger: submit_order immediately after subscribing: the subscription handshake completed (profiler created, is_initialized true) but the event backfill has processed zero events — a brand-new pool, an indexer still backfilling, or a quiet pool with no events in the replay window.

Common situations: Strategies firing on a timer at startup rather than on data arrival; low-activity pools where the first Mint/Swap event has not been observed yet; data client still replaying historical logs from the configured start block.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21). Data as JSON: /api/errors/08fc50b9872a9678. Report an issue: GitHub.