nautechsystems/nautilus_trader · error

cannot advance an RPC profiler without a snapshot watermark

Error message

cannot advance an RPC profiler without a snapshot watermark

What it means

advance_pool_profiler_from_rpc_snapshot requires the incoming profiler to carry a last_processed_event watermark — the snapshot position from which RPC-derived events will be advanced to to_block. If the profiler has no watermark (never snapshotted or built only from live data), advancement is impossible and this invariant error is returned.

Source

Thrown at crates/adapters/blockchain/src/data/core.rs:1609

    }

    /// Advances an RPC-hydrated profiler to a later checkpoint.
    ///
    /// The profiler must come from [`Self::bootstrap_pool_profiler_from_rpc_snapshot`] or an earlier
    /// call to this method. This keeps one command incremental without trusting an unproven stored
    /// snapshot as the topology source.
    ///
    /// # Errors
    ///
    /// Returns an error if the profiler has no RPC snapshot watermark, the target precedes that
    /// watermark, event streaming fails, or RPC hydration fails.
    pub async fn advance_pool_profiler_from_rpc_snapshot(
        &mut self,
        profiler: PoolProfiler,
        to_block: u64,
    ) -> anyhow::Result<(PoolProfiler, bool)> {
        let from_position = profiler.last_processed_event.clone().ok_or_else(|| {
            anyhow::anyhow!("cannot advance an RPC profiler without a snapshot watermark")
        })?;

        if to_block < from_position.number {
            anyhow::bail!(
                "cannot advance RPC profiler from block {} to earlier block {to_block}",
                from_position.number
            );
        }

        self.construct_pool_profiler_from_hypersync_rpc(profiler, Some(from_position), to_block)
            .await
    }

    async fn seed_pool_profiler_from_latest_snapshot(
        &self,
        pool: &SharedPool,
        to_block: u64,
    ) -> anyhow::Result<(PoolProfiler, Option<BlockPosition>)> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Initialize the profiler from a persisted snapshot that includes last_processed_event before calling advance.
  2. If no snapshot exists, use the full bootstrap path (sync_pool_events / bootstrap_latest_pool_profiler) instead of the RPC advancement path.
  3. Check that the state store round-trips last_processed_event correctly when saving/loading the profiler.
  4. Guard the call site: only advance when profiler.last_processed_event.is_some().

Example fix

// before
let (profiler, _) = core.advance_pool_profiler_from_rpc_snapshot(new_profiler, head).await?;

// after
if new_profiler.last_processed_event.is_none() {
    let (new_profiler, _) = core.bootstrap_latest_pool_profiler(...).await?;
}
let (profiler, _) = core.advance_pool_profiler_from_rpc_snapshot(new_profiler, head).await?;
Defensive patterns

Strategy: validation

Validate before calling

// guard before calling the RPC advancement path
if profiler.last_processed_event.is_none() {
    return Err(anyhow::anyhow!(
        "profiler has no snapshot watermark; run bootstrap_latest_pool_profiler first"
    ));
}

Type guard

fn has_watermark(p: &PoolProfiler) -> bool {
    p.last_processed_event.is_some()
}

Prevention

When it happens

Trigger: Passing a fresh PoolProfiler whose last_processed_event is None (built locally, not deserialized from a snapshot store) into advance_pool_profiler_from_rpc_snapshot.

Common situations: Calling the RPC advancement path with a profiler constructed in-memory instead of one restored from persisted profiler state; version drift where older stored snapshots lack the watermark field.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — 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/e9303753143540e2. Report an issue: GitHub.