{"record":{"id":"321fe859b72be8a6","repo":"nautechsystems/nautilus_trader","slug":"failed-to-lock-finalized-header-tip-e","errorCode":null,"errorMessage":"Failed to lock finalized header tip: {e}","messagePattern":"Failed to lock finalized header tip: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":4432,"sourceCode":"            \"Finalized checkpoint ledger conflicts with the trusted chain anchor\"\n        );\n\n        if initialized {\n            let stored_tip =\n                sqlx::query_as::<_, (i64, String, String, i64, Option<String>, String)>(\n                    \"\n                SELECT number, hash, parent_hash, timestamp, base_fee_per_gas, manifest_digest\n                FROM execution_verified_finalized_header\n                WHERE chain_id = $1 AND wallet_address = $2\n                ORDER BY number DESC\n                LIMIT 1\n                \",\n                )\n                .bind(chain_id)\n                .bind(bootstrap.wallet_address)\n                .fetch_one(&mut *transaction)\n                .await\n                .map_err(|e| anyhow::anyhow!(\"Failed to lock finalized header tip: {e}\"))?;\n            anyhow::ensure!(\n                stored_tip\n                    == (\n                        i64::try_from(first_header.number)\n                            .context(\"Verified finalized height exceeds PostgreSQL BIGINT\")?,\n                        first_header.hash.clone(),\n                        first_header.parent_hash.clone(),\n                        i64::try_from(first_header.timestamp)\n                            .context(\"Verified finalized timestamp exceeds PostgreSQL BIGINT\")?,\n                        first_header.base_fee_per_gas.map(|value| value.to_string()),\n                        bootstrap.manifest_digest.to_string(),\n                    ),\n                \"Verified finalized header extension does not start at the durable tip\"\n            );\n        } else {\n            anyhow::ensure!(\n                first_header.number == bootstrap.checkpoint_number\n                    && first_header.hash == bootstrap.checkpoint_hash","sourceCodeStart":4414,"sourceCodeEnd":4450,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L4414-L4450","documentation":"This error wraps a sqlx failure on the SELECT that locks/reads the current tip (highest `number`) of `execution_verified_finalized_header` when the ledger is already initialized. The code fetches the latest stored header to confirm the incoming header extension starts exactly at the durable tip; if that fetch fails (no rows, table missing, connection error) the error is raised with this message and initialization aborts.","triggerScenarios":"During re-initialization of an existing ledger: the tip SELECT (ORDER BY number DESC LIMIT 1, fetch_one) returns zero rows because the table was truncated between the initialized flag and this query; schema drift; connection loss mid-transaction.","commonSituations":"Another process or job deleted all verified-header rows for this (chain_id, wallet_address) after initialization; running two adapter instances against the same ledger where one wiped rows; Postgres failover killing the transaction; deploying against a half-migrated schema.","solutions":["Read the wrapped sqlx error: RowNotFound means the ledger has no rows — reset the `initialized` state (or the whole wallet ledger) so bootstrap takes the fresh-checkpoint path instead of the tip-extension path.","Check for concurrent writers/cleaners touching `execution_verified_finalized_header`; serialize access or use advisory locking between adapter instances.","Run schema migrations if the error indicates missing table/columns.","Verify connection stability and statement timeouts; re-run initialization — it is transactional and idempotent."],"exampleFix":"// before\n.fetch_one(&mut *transaction)\n.await\n.map_err(|e| anyhow::anyhow!(\"Failed to lock finalized header tip: {e}\"))?;\n\n// after\n.fetch_one(&mut *transaction)\n.await\n.map_err(|e| {\n    anyhow::anyhow!(\n        \"Failed to lock finalized header tip for chain {chain_id}: {e:#}\"\n    )\n})?;","handlingStrategy":"validation","validationCode":"// Confirm tip row presence before the tip-extension path\nlet tip: Option<(i64, String)> = sqlx::query_as(\n    \"SELECT number, hash FROM execution_verified_finalized_header WHERE chain_id=$1 AND wallet_address=$2 ORDER BY number DESC LIMIT 1\",\n).bind(chain_id).bind(wallet).fetch_optional(&pool).await?;\nanyhow::ensure!(\n    tip.is_some(),\n    \"Ledger marked initialized but has no headers — reset initialization state so bootstrap takes the fresh-checkpoint path\"\n);","typeGuard":null,"tryCatchPattern":"match result {\n    Err(sqlx::Error::RowNotFound) => {\n        warn!(\"tip row missing while initialized=true; falling back to fresh checkpoint bootstrap\");\n        initialize_from_checkpoint(bootstrap)\n    }\n    Err(e) => Err(anyhow!(\"tip lock failed: {e:#}\")),\n    Ok(tip) => validate_tip_extension(tip),\n}","preventionTips":["Do not run multiple adapter instances against the same (chain_id, wallet_address) ledger","Block external deletion/truncation of execution_verified_finalized_header while initialized","Take the fresh-checkpoint path when the ledger is empty rather than assuming a tip exists","Monitor for DB failovers and restart the bootstrap transaction cleanly"],"tags":["database","sqlx","postgres","ledger"],"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-14T05:17:10.506Z"}