{"record":{"id":"05e0d27e69e9f287","repo":"nautechsystems/nautilus_trader","slug":"verified-finalized-header-tip-does-not-match-the-l","errorCode":null,"errorMessage":"Verified finalized header tip does not match the ledger","messagePattern":"Verified finalized header tip does not match the ledger","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":6973,"sourceCode":"            \"Finalized nonce {} does not match canonical nonce {stored_nonce}\",\n            finality.nonce\n        );\n        let stored_tip = 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(finality.wallet_address)\n        .fetch_one(&mut *transaction)\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Failed to lock finalized header tip: {e}\"))?;\n        let first_header = &finality.finalized_headers[0];\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                    finality.manifest_digest.to_string(),\n                ),\n            \"Verified finality extension does not start at the durable tip\"\n        );\n\n        for header in finality.finalized_headers.iter().skip(1) {\n            let number = i64::try_from(header.number)\n                .context(\"Verified finalized height exceeds PostgreSQL BIGINT\")?;\n            let timestamp = i64::try_from(header.timestamp)","sourceCodeStart":6955,"sourceCodeEnd":6991,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L6955-L6991","documentation":"Thrown by `record_execution_finality_verified` when the first header of finality.finalized_headers does not exactly equal the durable tip stored in execution_verified_finalized_header (number, hash, parent_hash, timestamp, base_fee_per_gas, manifest_digest are all compared as a tuple). This guard ensures each new finality extension continues exactly from the previously persisted chain tip — no forks, reorgs, or gaps are accepted. The adjacent message about the ledger tip (\"Verified finality extension does not start at the durable tip\") is the paired ensure at line 6985.","triggerScenarios":"Calling record_execution_finality_verified when finality.finalized_headers[0] differs from the stored tip in any field: the batch starts at a later/earlier height than the tip, continues from a different parent (fork/reorg), or any header field (hash, parent_hash, timestamp, base_fee_per_gas, manifest_digest) diverges from what was persisted.","commonSituations":"A chain reorg replaced the header at the tip height so hashes no longer match; recording finality batches with overlaps or gaps (tip is height N but the new batch starts at N+2 or N); an incompatible manifest digest after a manifest change; a clock/base-fee normalization difference making stored and incoming field encodings differ.","solutions":["Align the finalized_headers batch to begin exactly at the stored tip: headers[0] must be the persisted tip header itself, with subsequent headers extending it contiguously","Check for a reorg; if the stored tip is stale or invalid, rebuild the verified finalized header chain from the canonical chain before recording","Ensure the manifest digest is unchanged (a digest mismatch also fails this tuple comparison); re-verify under the stored manifest","Verify field encodings match (base_fee_per_gas string formatting, timestamp units) so the tuple equality holds for identical headers"],"exampleFix":"// before: batch skips past the durable tip\nlet headers = fetch_finalized_headers(start = tip.number + 2);\ncache.record_execution_finality_verified(&finality_with(headers)).await?;\n\n// after: include the tip header as the first element of the extension\nlet headers = fetch_finalized_headers(start = tip.number);\nassert_eq!(headers[0].hash, tip.hash);\ncache.record_execution_finality_verified(&finality_with(headers)).await?;","handlingStrategy":"validation","validationCode":"let (tip_number, tip_hash): (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)\n.bind(chain_id).bind(wallet_address).fetch_one(&pool).await?;\nlet first = &finality.finalized_headers[0];\nanyhow::ensure!(\n    first.hash == tip_hash && first.number == tip_number as u64,\n    \"Finality batch must extend the durable tip exactly\"\n);","typeGuard":null,"tryCatchPattern":"match cache.record_execution_finality_verified(&finality).await {\n    Err(e) if e.to_string().contains(\"does not start at the durable tip\") => {\n        // detect reorg: compare parent hashes and rebuild the header chain if needed\n    }\n    result => result?,\n}","preventionTips":["Build each finality batch to start at the persisted tip height and hash, chaining headers contiguously","Monitor for chain reorgs and rebuild verified header state after any detected reorg depth","Keep header field encoding (base fee formatting, timestamp units) consistent between recorder and persister"],"tags":["database","integrity-check","blockchain","rust"],"backgroundTag":"invalid-state-transition","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"}