{"record":{"id":"7cfca296442a60bd","repo":"nautechsystems/nautilus_trader","slug":"failed-to-get-table-last-block-for-table-name","errorCode":null,"errorMessage":"Failed to get table last block for {table_name}: {e}","messagePattern":"Failed to get table last block for (.+?): (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":1694,"sourceCode":"    ///\n    /// # Errors\n    ///\n    /// Returns an error if the database query fails.\n    pub async fn get_table_last_block(\n        &self,\n        chain_id: u32,\n        table_name: &str,\n        pool_identifier: &PoolIdentifier,\n    ) -> anyhow::Result<Option<u64>> {\n        let query = format!(\n            \"SELECT MAX(block) FROM {table_name} WHERE chain_id = $1 AND pool_identifier = $2\"\n        );\n        let result = sqlx::query_as::<_, (Option<i64>,)>(AssertSqlSafe(query))\n            .bind(chain_id as i32)\n            .bind(pool_identifier.as_ref())\n            .fetch_optional(&self.pool)\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Failed to get table last block for {table_name}: {e}\"))?;\n\n        Ok(result.and_then(|(block_number,)| block_number.map(|b| b as u64)))\n    }\n\n    /// Adds a batch of pool fee collect events to the database using batch operations.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the database operation fails.\n    pub async fn add_pool_collects_batch(\n        &self,\n        chain_id: u32,\n        collects: &[PoolFeeCollect],\n    ) -> anyhow::Result<()> {\n        if collects.is_empty() {\n            return Ok(());\n        }\n","sourceCodeStart":1676,"sourceCodeEnd":1712,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L1676-L1712","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the event-family table exists (migrations or table creation for the family) before querying","Verify the table_name string matches the actual Postgres table (case/qualifier)","Check connectivity and retry the idempotent read","Inspect the wrapped sqlx error — `relation ... does not exist` points to a missing table"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Ensure the event-family table exists before querying it\nif !KNOWN_EVENT_FAMILIES.contains(&table_name) {\n    return Err(anyhow::anyhow!(\"unknown event family table: {table_name}\"));\n}\nsqlx::query(&format!(\"SELECT 1 FROM {table_name} LIMIT 1\")).fetch_optional(&pool).await?;","typeGuard":null,"tryCatchPattern":"match get_table_last_block(table_name, chain_id, pool_id).await {\n    Ok(b) => b.unwrap_or(0),\n    Err(e) if e.to_string().contains(\"does not exist\") => 0, // empty family table\n    Err(e) => return Err(e),\n}","preventionTips":["Whitelist table names against known event families before interpolation","Create family tables lazily before first query","Migrate schema for every supported event family"],"tags":["database","sqlx","dynamic-sql","read-failure"],"backgroundTag":"sql-query-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}