{"record":{"id":"fe28b07ee29d5d32","repo":"nautechsystems/nautilus_trader","slug":"verified-finality-headers-must-form-a-continuous-c","errorCode":null,"errorMessage":"Verified finality headers must form a continuous chain through the inclusion height","messagePattern":"Verified finality headers must form a continuous chain through the inclusion height","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":6906,"sourceCode":"    ///\n    /// Returns an error if the receipt transition, nonce ledger, manifest identity, or evidence\n    /// is inconsistent, or if persistence fails.\n    pub(crate) async fn record_execution_finality_verified(\n        &self,\n        finality: &ExecutionFinalityTransition<'_>,\n    ) -> anyhow::Result<()> {\n        anyhow::ensure!(\n            matches!(\n                finality.status,\n                TransactionStatus::Finalized | TransactionStatus::Reverted\n            ),\n            \"Verified finality requires a finalized or reverted status\"\n        );\n        anyhow::ensure!(\n            !finality.decisions.is_empty(),\n            \"Verified finality requires decision evidence\"\n        );\n        anyhow::ensure!(\n            !finality.finalized_headers.is_empty()\n                && finality.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                && finality\n                    .finalized_headers\n                    .last()\n                    .is_some_and(|header| header.number >= finality.block_number),\n            \"Verified finality headers must form a continuous chain through the inclusion height\"\n        );\n        let chain_id = i32::try_from(finality.chain_id)\n            .context(\"Verification chain ID exceeds PostgreSQL INTEGER\")?;\n        let nonce =\n            i64::try_from(finality.nonce).context(\"Execution nonce exceeds PostgreSQL BIGINT\")?;\n        let next_nonce = nonce\n            .checked_add(1)\n            .ok_or_else(|| anyhow::anyhow!(\"Canonical nonce overflow\"))?;","sourceCodeStart":6888,"sourceCodeEnd":6924,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L6888-L6924","documentation":"This error fires when the `finalized_headers` slice attached to a verified finality transition is empty, is not strictly sequential (each header's number must be the previous plus one and each must be the previous header's parent-hash child), or its last header does not reach the transaction's inclusion block height. The database layer requires an unbroken, hash-linked header chain proving canonicality up to the inclusion block before persisting finality. It is a fail-fast precondition checked before any SQL executes.","triggerScenarios":"Calling record_execution_finality_verified with an empty finalized_headers slice; headers fetched with a gap (e.g. reorg removed a block); headers in the wrong order; header.number < finality.block_number for the last header; parent_hash not matching the previous header's hash.","commonSituations":"RPC provider returned a partial or reorged header range; caching layer deduplicated a header that was actually required for continuity; a reorg happened between finality decision and header fetch; test fixture headers hand-built without linking parent hashes; off-by-one so the last header stops one block short of the inclusion height.","solutions":["Re-fetch the finalized header range from the node and verify each header's number is contiguous and each parent_hash links to the previous header's hash before calling the API","Ensure the header range extends at least to finality.block_number (the inclusion height)","Re-order headers oldest-to-newest if they were collected newest-first","If a reorg invalidated the chain, rebuild the finality transition from the current canonical chain instead of reusing stale headers"],"exampleFix":"// before\nlet headers = client.finalized_headers(from..to).await?; // may contain gaps after reorg\ndb.record_execution_finality_verified(&finality_with(headers)).await?;\n// after\nlet headers = client.finalized_headers(from..=finality.block_number).await?;\nassert!(windows(2).all(|h| h[1].number == h[0].number + 1 && h[1].parent_hash == h[0].hash));\ndb.record_execution_finality_verified(&finality_with(headers)).await?;","handlingStrategy":"validation","validationCode":"// Rust: validate the header chain before persisting finality\nfn headers_form_chain(headers: &[ExecutionVerifiedHeader], inclusion: u64) -> bool {\n    !headers.is_empty()\n        && headers.windows(2).all(|h| {\n            h[1].number == h[0].number.saturating_add(1) && h[1].parent_hash == h[0].hash\n        })\n        && headers.last().is_some_and(|h| h.number >= inclusion)\n}\nensure!(headers_form_chain(finality.finalized_headers, finality.block_number));","typeGuard":"fn is_contiguous_header_chain(headers: &[ExecutionVerifiedHeader]) -> bool {\n    headers.windows(2).all(|h| h[1].parent_hash == h[0].hash && h[1].number == h[0].number + 1)\n}","tryCatchPattern":"match db.record_execution_finality_verified(&finality).await {\n    Err(e) if e.to_string().contains(\"continuous chain\") => {\n        // re-fetch headers from the node and rebuild the transition before retrying\n    }\n    other => other?,\n}","preventionTips":["Fetch the header range as `inclusion_height - n ..= inclusion_height` from a single consistent RPC snapshot","Validate parent-hash linkage immediately after fetching, before building the transition","Detect reorgs (header number/hash mismatch on refetch) and rebuild finality from the canonical chain","Keep headers sorted ascending by number when aggregating from multiple sources"],"tags":["validation","database","consistency","blockchain"],"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-14T05:17:10.506Z"}