nautechsystems/nautilus_trader · error · anyhow::Error

Failed to get block info for chain {}: {}

Error message

Failed to get block info for chain {}: {}

What it means

This error is raised when the query fetching the chain's cached block info (e.g. latest block number/timestamp used as the sync watermark) fails or the sqlx fetch_one errors. Note fetch_one also errors when zero rows are returned, so an unseeded/empty chain state also produces this message.

Source

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

    /// Returns an error if the database query fails.
    pub async fn get_block_consistency_status(
        &self,
        chain: &Chain,
    ) -> anyhow::Result<CachedBlocksConsistencyStatus> {
        log::debug!("Fetching block consistency status");

        let result: (i64, i64) = sqlx::query_as(
            "
            SELECT
                COALESCE((SELECT number FROM block WHERE chain_id = $1 ORDER BY number DESC LIMIT 1), 0) as max_block,
                get_last_continuous_block($1) as last_continuous_block
            "
        )
        .bind(chain.chain_id as i32)
        .fetch_one(&self.pool)
        .await
        .map_err(|e| {
            anyhow::anyhow!(
                "Failed to get block info for chain {}: {}",
                chain.chain_id,
                e
            )
        })?;

        Ok(CachedBlocksConsistencyStatus::new(
            result.0 as u64,
            result.1 as u64,
        ))
    }

    /// Inserts or updates a block record in the database.
    ///
    /// # Errors
    ///
    /// Returns an error if the database operation fails.
    pub async fn add_block(&self, chain_id: u32, block: &Block) -> anyhow::Result<()> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the {e} source: QueryReturnedNoRows means the chain has no cached block info yet — seed or sync the chain first
  2. Verify migrations/partitions exist for this chain_id
  3. Confirm connection settings and that the correct database is targeted
  4. Handle the empty case explicitly in the caller if starting from scratch is expected

Example fix

// before: treats empty cache as hard failure
let info = db.get_block_info(&chain).await?;
// after: fall back to a starting block when nothing is cached
let info = match db.get_block_info(&chain).await {
    Ok(info) => info,
    Err(e) if is_empty_result(&e) => BlockInfo::default_start(chain),
    Err(e) => return Err(e.into()),
};
Defensive patterns

Strategy: fallback

Validate before calling

// check the chain has cached state before reading block info
let has_row: (i64,) = sqlx::query_as(
    "SELECT COUNT(*) FROM block WHERE chain_id = $1"
).bind(chain.chain_id as i32).fetch_one(&pool).await?;
let cache_populated = has_row.0 > 0;

Try / catch

let start = match db.get_block_info(&chain).await {
    Ok(info) => info.next_block(),
    Err(e) if is_query_returned_no_rows(&e) => chain.start_block(), // fresh cache
    Err(e) => return Err(e.into()),
};

Prevention

When it happens

Trigger: Calling the get-block-info method for a chain when the row is missing (chain never synced) or the SELECT fails (connection, permissions, missing table/partition).

Common situations: Pointing the adapter at a fresh database where the chain has no cached blocks; wrong DATABASE_URL/environment; sqlx row-type mismatch after a schema change.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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