nautechsystems/nautilus_trader · error · anyhow::Error

Failed to get table last block for {table_name}: {e}

Error message

Failed to get table last block for {table_name}: {e}

What it means

Wraps a sqlx error from a dynamically built SELECT that reads the last block stored in a specific per-event-family table (table_name interpolated into the query and guarded by AssertSqlSafe). Failure aborts determining where that family's stored events end.

Source

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

    ///
    /// # Errors
    ///
    /// Returns an error if the database query fails.
    pub async fn get_table_last_block(
        &self,
        chain_id: u32,
        table_name: &str,
        pool_identifier: &PoolIdentifier,
    ) -> anyhow::Result<Option<u64>> {
        let query = format!(
            "SELECT MAX(block) FROM {table_name} WHERE chain_id = $1 AND pool_identifier = $2"
        );
        let result = sqlx::query_as::<_, (Option<i64>,)>(AssertSqlSafe(query))
            .bind(chain_id as i32)
            .bind(pool_identifier.as_ref())
            .fetch_optional(&self.pool)
            .await
            .map_err(|e| anyhow::anyhow!("Failed to get table last block for {table_name}: {e}"))?;

        Ok(result.and_then(|(block_number,)| block_number.map(|b| b as u64)))
    }

    /// Adds a batch of pool fee collect events to the database using batch operations.
    ///
    /// # Errors
    ///
    /// Returns an error if the database operation fails.
    pub async fn add_pool_collects_batch(
        &self,
        chain_id: u32,
        collects: &[PoolFeeCollect],
    ) -> anyhow::Result<()> {
        if collects.is_empty() {
            return Ok(());
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the event-family table exists (migrations or table creation for the family) before querying
  2. Verify the table_name string matches the actual Postgres table (case/qualifier)
  3. Check connectivity and retry the idempotent read
  4. Inspect the wrapped sqlx error — `relation ... does not exist` points to a missing table
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the event-family table exists before querying it
if !KNOWN_EVENT_FAMILIES.contains(&table_name) {
    return Err(anyhow::anyhow!("unknown event family table: {table_name}"));
}
sqlx::query(&format!("SELECT 1 FROM {table_name} LIMIT 1")).fetch_optional(&pool).await?;

Try / catch

match get_table_last_block(table_name, chain_id, pool_id).await {
    Ok(b) => b.unwrap_or(0),
    Err(e) if e.to_string().contains("does not exist") => 0, // empty family table
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `get_table_last_block(table_name, chain_id, pool_identifier)` when the per-family table does not exist (AssertSqlSafe passes the name through but the server errors on a missing relation), the connection fails, or the query is cancelled.

Common situations: Event-family table not yet created by migrations; passing a table_name that does not match a real table; DB restart or timeout during event backfill.

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


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