{"record":{"id":"d3eaa695d64a8e2c","repo":"nautechsystems/nautilus_trader","slug":"verified-finalized-header-ledger-is-not-continuous","errorCode":null,"errorMessage":"Verified finalized header ledger is not continuous","messagePattern":"Verified finalized header ledger is not continuous","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":7742,"sourceCode":"                    );\n                    Ok(ExecutionVerifiedHeader {\n                        number: u64::try_from(number)\n                            .context(\"Finalized header number is negative\")?,\n                        hash,\n                        parent_hash,\n                        timestamp: u64::try_from(timestamp)\n                            .context(\"Finalized header timestamp is negative\")?,\n                        base_fee_per_gas: base_fee\n                            .map(|value| {\n                                value.parse::<u128>().map_err(|_| {\n                                    anyhow::anyhow!(\"Finalized header base fee is invalid\")\n                                })\n                            })\n                            .transpose()?,\n                    })\n                })\n                .collect::<anyhow::Result<Vec<_>>>()?;\n            anyhow::ensure!(\n                !finalized_headers.is_empty()\n                    && finalized_headers.windows(2).all(|headers| {\n                        headers[1].number == headers[0].number.saturating_add(1)\n                            && headers[1].parent_hash == headers[0].hash\n                    }),\n                \"Verified finalized header ledger is not continuous\"\n            );\n            Ok(Some(ExecutionVerificationResume {\n                next_canonical_nonce: u64::try_from(nonce)\n                    .context(\"Canonical nonce is negative\")?,\n                revision: u64::try_from(revision)\n                    .context(\"Canonical nonce revision is negative\")?,\n                finalized_headers,\n            }))\n        }\n    }\n\n    #[tokio::test]","sourceCodeStart":7724,"sourceCodeEnd":7760,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L7724-L7760","documentation":"After deserializing the verified finalized-header ledger, the library verifies the chain is continuous: it must be non-empty and every header must extend the previous one (number incremented by exactly 1 and parent_hash equal to the previous header's hash). This error is thrown when those invariants fail, because a discontinuous ledger cannot be trusted for execution verification.","triggerScenarios":"Loading a ledger with zero rows, rows with gaps in numbers (e.g. 100, 102), or rows whose parent_hash does not match the previous row's hash — typically after manual deletes, a partial re-ingestion, or mixing rows from different manifests/chains.","commonSituations":"Deleting individual header rows to 'clean up' the table; a failed backfill that skipped blocks; rows from a reorged or forked chain interleaved with the canonical ledger; a wiped table being partially repopulated.","solutions":["Re-ingest the full finalized-header ledger from the chain under the current manifest so the sequence is rebuilt contiguously","Restore the ledger from a backup taken before the discontinuity was introduced","Identify the gap/mismatch (compare consecutive number/parent_hash values) and backfill the missing headers in order","Never delete individual rows; truncate the whole ledger and repopulate atomically if cleanup is needed"],"exampleFix":"// before\nDELETE FROM finalized_headers WHERE number = 101; -- creates a gap\n// after\nTRUNCATE finalized_headers; -- then rebuild the full contiguous ledger from source","handlingStrategy":"try-catch","validationCode":"let gaps = sqlx::query(\"\n    SELECT a.number FROM finalized_headers a\n    LEFT JOIN finalized_headers b ON b.number = a.number + 1\n    WHERE b.number IS NULL\")\n    .fetch_all(&pool).await?;\nif !gaps.is_empty() { eprintln!(\"ledger discontinuities at {:?}\", gaps); }","typeGuard":"fn ledger_is_continuous(headers: &[ExecutionVerifiedHeader]) -> bool {\n    !headers.is_empty()\n        && headers.windows(2).all(|w| {\n            w[1].number == w[0].number + 1 && w[1].parent_hash == w[0].hash\n        })\n}","tryCatchPattern":"match load_finalized_ledger(digest).await {\n    Err(e) if e.to_string().contains(\"not continuous\") => {\n        // truncate and rebuild the ledger from the chain\n    }\n    other => other?,\n}","preventionTips":["Never delete individual ledger rows; rebuild atomically","Write ledger rows in ordered batches within one transaction","Detect reorgs/forks before appending headers","Run continuity checks as a periodic integrity job"],"tags":["database","consistency","blockchain","ledger"],"backgroundTag":"internal-invariant-violation","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"}