nautechsystems/nautilus_trader · error · anyhow::Error

Missing dex_name for pool snapshot {}

Error message

Missing dex_name for pool snapshot {}

What it means

The snapshot row has a NULL `dex_name` column, but a DEX name is mandatory to resolve the chain's DEX registry entry and build the pool instrument. The library treats a snapshot without a DEX name as corrupt/incomplete and refuses to load it.

Source

Thrown at crates/adapters/blockchain/src/cache/database.rs:2533

                    transaction_index,
                    log_index,
                )
                .await?;

            let ticks = self
                .load_pool_ticks_for_snapshot(
                    chain_id,
                    pool_identifier,
                    block,
                    transaction_index,
                    log_index,
                )
                .await?;

            let dex_name = row
                .try_get::<Option<String>, _>("dex_name")?
                .ok_or_else(|| {
                    anyhow::anyhow!("Missing dex_name for pool snapshot {}", pool_identifier)
                })?;
            let chain = Chain::from_chain_id(chain_id)
                .ok_or_else(|| anyhow::anyhow!("Unknown chain_id: {chain_id}"))?;

            let dex_type = DexType::from_dex_name(&dex_name)
                .ok_or_else(|| anyhow::anyhow!("Unknown dex_name: {dex_name}"))?;

            let dex_extended = crate::exchanges::get_dex_extended(chain.name, &dex_type)
                .ok_or_else(|| {
                    anyhow::anyhow!("No DEX extended found for {} on {}", dex_name, chain.name)
                })?;

            let instrument_id =
                Pool::create_instrument_id(chain.name, &dex_extended.dex, pool_identifier.as_ref());

            Ok(Some(PoolSnapshot::new(
                instrument_id,
                state,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Backfill `dex_name` for existing rows from pool metadata (or delete affected snapshots and re-snapshot).
  2. Add a NOT NULL constraint on `dex_name` so bad rows are rejected at write time.
  3. Ensure the snapshot writer always records `dex_name` before INSERT (unit-test the persist path).
  4. If rows came from an import/ETL job, fix the job to include dex_name and re-run.

Example fix

// schema
// before: dex_name TEXT
// after
ALTER TABLE pool_snapshots ALTER COLUMN dex_name SET NOT NULL;
Defensive patterns

Strategy: validation

Validate before calling

let dex: Option<String> = sqlx::query_scalar(
    "SELECT dex_name FROM pool_snapshots WHERE pool_identifier = $1 ORDER BY block DESC LIMIT 1")
    .bind(pool_id).fetch_optional(&db).await?;
if dex.as_deref().unwrap_or_default().is_empty() {
    return Err("snapshot missing dex_name; backfill or re-snapshot");
}

Try / catch

match load_snapshot(...).await {
    Err(e) if e.to_string().contains("Missing dex_name") => {
        tracing::warn!("corrupt snapshot row, re-snapshotting");
        re_snapshot(pool_id).await
    }
    other => other,
}

Prevention

When it happens

Trigger: Loading a pool snapshot row where `dex_name` is NULL — rows written before the column existed, partially written rows, manual inserts, or a writer path that failed to record the DEX name.

Common situations: Pre-migration legacy snapshot rows lacking the newly added `dex_name` column; manual DB seeding during testing; partial writes from an interrupted persistence job.

Related errors


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