nautechsystems/nautilus_trader · critical

database presence is checked by caller

Error message

database presence is checked by caller

What it means

This is an internal invariant panic: seed_pool_profiler_from_latest_snapshot asserts via expect() that cache.database is Some before loading the latest pool snapshot. The function's contract requires callers (e.g. bootstrap_latest_pool_profiler) to have already verified that a database is configured. If the invariant is broken, the process panics instead of returning an error.

Source

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

            );
        }

        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>)> {
        let mut profiler = PoolProfiler::new(pool.clone());

        let from_position = match self
            .cache
            .database
            .as_ref()
            .expect("database presence is checked by caller")
            .load_latest_pool_snapshot(
                pool.chain.chain_id,
                &pool.pool_identifier,
                Some(to_block),
                true,
            )
            .await
        {
            Ok(Some(snapshot)) => {
                // Empty snapshots at the pool's creation block are stubs left behind by an
                // earlier bootstrap that bailed before any liquidity events landed. Restoring
                // marks the profiler as initialized, which then conflicts with the Initialize
                // event that hypersync re-emits at the same block. Fall through to a fresh
                // bootstrap rather than trust the stub.
                if snapshot.positions.is_empty()
                    && snapshot.ticks.is_empty()
                    && snapshot.block_position.number == pool.creation_block
                {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Configure the cache with a database before calling bootstrap_latest_pool_profiler / seed_pool_profiler_from_latest_snapshot.
  2. Add an explicit guard in the caller (e.g. ensure cache.database.is_some(), otherwise construct a database or return an error) before invoking the seeder.
  3. If you maintain this code, convert the expect into an anyhow::bail! or Option check so callers get a recoverable error instead of a panic.

Example fix

// before
let snapshot = self.cache.database.as_ref().expect("database presence is checked by caller")
    .load_latest_pool_snapshot(...)?;
// after
let database = self.cache.database.as_ref().ok_or_else(|| {
    anyhow::anyhow!("database not configured in cache; cannot seed pool profiler")
})?;
let snapshot = database.load_latest_pool_snapshot(...)?;
Defensive patterns

Strategy: validation

Validate before calling

if engine.cache.database.is_none() {
    anyhow::bail!("cache has no database configured; cannot bootstrap pool profiler");
}
engine.bootstrap_latest_pool_profiler(&pool, to_block)?;

Type guard

fn has_database(cache: &Cache) -> bool {
    cache.database.is_some()
}

Try / catch

// Rust panics are not catchable via Result; use std::panic::catch_unwind only as a last resort.
let result = std::panic::catch_unwind(|| engine.bootstrap_latest_pool_profiler(&pool, to_block));
match result {
    Ok(inner) => inner?,
    Err(_) => anyhow::bail!("panic in profiler bootstrap: database not configured"),
}

Prevention

When it happens

Trigger: Calling seed_pool_profiler_from_latest_snapshot (directly or via bootstrap_latest_pool_profiler) on a data engine whose cache was constructed with database: None, so cache.database.as_ref() yields None at crates/adapters/blockchain/src/data/core.rs:1634.

Common situations: Running the pool-profiler bootstrap path in a deployment or test harness where the cache was built without a database backend (e.g. no persistence configured, or a refactored constructor dropped the database argument); wiring a custom cache implementation that does not populate the database field.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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