{"record":{"id":"eb43c98526c67832","repo":"nautechsystems/nautilus_trader","slug":"failed-to-load-block-timestamps-e","errorCode":null,"errorMessage":"Failed to load block timestamps: {e}","messagePattern":"Failed to load block timestamps: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":761,"sourceCode":"                number,\n                timestamp\n            FROM (\n                SELECT number, timestamp, 0 AS source_order\n                FROM block\n                WHERE chain_id = $1 AND number >= $2 AND timestamp IS NOT NULL\n                UNION ALL\n                SELECT number, timestamp, 1 AS source_order\n                FROM pool_event_block\n                WHERE chain_id = $1 AND number >= $2 AND timestamp IS NOT NULL\n            ) AS block_timestamps\n            ORDER BY number ASC, source_order ASC\n            \",\n        )\n        .bind(chain.chain_id as i32)\n        .bind(from_block as i64)\n        .fetch_all(&self.pool)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to load block timestamps: {e}\"))\n    }\n\n    /// Adds or updates a DEX (Decentralized Exchange) record in the database.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the database operation fails.\n    pub async fn add_dex(&self, dex: SharedDex) -> anyhow::Result<()> {\n        sqlx::query(\n            \"\n            INSERT INTO dex (\n                chain_id, name, factory_address, creation_block\n            ) VALUES ($1, $2, $3, $4)\n            ON CONFLICT (chain_id, name)\n            DO UPDATE\n            SET\n                factory_address = $3,\n                creation_block = $4","sourceCodeStart":743,"sourceCodeEnd":779,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L743-L779","documentation":"The query that loads block timestamps for a chain within a block range (`WHERE chain_id = $1 AND number >= $2 ...`) failed; the sqlx error is wrapped with this message. This is a read-path error surfaced while the indexer needs timestamps for historical pool events.","triggerScenarios":"Calling the block-timestamp loader with a chain_id/from_block that exposes a cast problem (chain_id exceeding i32), the `block` or `pool_event_block` table missing, connection failure/timeout during `.fetch_all`, or a type mismatch between the query's expected columns and the actual schema.","commonSituations":"Pointing the service at a fresh/empty database where migrations never ran; wrong DATABASE_URL pointing to another service's schema; Postgres restart mid-query; chain_id values beyond i32 range on exotic chains.","solutions":["Inspect the wrapped `{e}`: 'relation does not exist' → run migrations; 'connection refused' → fix DATABASE_URL/network","Verify migrations have been applied to the target database (check the migrations table) before querying","Validate chain_id fits in i32 and from_block fits in i64 before binding","Retry transient connection errors with backoff; increase the pool's acquire timeout if under load"],"exampleFix":"// before\nlet rows = loader.load_timestamps(chain, from_block).await?;\n// after: guard and retry transient failures\nif chain.chain_id > i32::MAX as u64 { return Err(anyhow::anyhow!(\"chain_id exceeds i32\")); }\nlet rows = retry_backoff(|| loader.load_timestamps(chain, from_block)).await?;","handlingStrategy":"retry","validationCode":"anyhow::ensure!(chain.chain_id <= i32::MAX as u64, \"chain_id exceeds i32\");\nanyhow::ensure!(from_block >= 0 && from_block <= i64::MAX, \"from_block out of i64 range\");\n// migrations applied?\nlet v: Option<i32> = sqlx::query_scalar(\"SELECT 1 FROM information_schema.tables WHERE table_name = 'block'\").fetch_optional(&pool).await?;\nanyhow::ensure!(v.is_some(), \"block table missing; run migrations\");","typeGuard":"fn query_args_valid(chain_id: u64, from_block: i64) -> bool {\n    chain_id <= i32::MAX as u64 && (0..=i64::MAX).contains(&from_block)\n}","tryCatchPattern":"let rows = match loader.load_block_timestamps(&chain, from_block).await {\n    Ok(r) => r,\n    Err(e) if is_transient(&e) => backoff_retry(|| loader.load_block_timestamps(&chain, from_block)).await?,\n    Err(e) => return Err(e),\n};","preventionTips":["Apply migrations as part of deployment/startup","Point DATABASE_URL at the correct database and verify with a startup smoke query","Add bounded retry with backoff for transient connection errors","Validate numeric ranges before binding to i32/i64 parameters"],"tags":["database","postgres","sqlx","query"],"backgroundTag":"database-query-failed","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}