{"record":{"id":"e6a831075bdfa51e","repo":"nautechsystems/nautilus_trader","slug":"verified-finality-requires-decision-evidence","errorCode":null,"errorMessage":"Verified finality requires decision evidence","messagePattern":"Verified finality requires decision evidence","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/cache/database.rs","lineNumber":6902,"sourceCode":"\n    /// Records verified final consumption and advances the canonical nonce in one transaction.\n    ///\n    /// # Errors\n    ///\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 =","sourceCodeStart":6884,"sourceCodeEnd":6920,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/cache/database.rs#L6884-L6920","documentation":"This error is thrown by record_execution_finality_verified in BlockchainCacheDatabase when a caller attempts to persist a verified finality transition whose `decisions` slice is empty. The database layer refuses to record finality without at least one verification-decision record, because decision evidence is the audit trail proving how the transaction reached Finalized/Reverted status. It is a fail-fast precondition check before any SQL runs, so nothing is written to the database when it fires.","triggerScenarios":"Calling record_execution_finality_verified (or the higher-level execution pipeline that routes into it) with an ExecutionFinalityTransition whose `decisions: &[...]` field is an empty slice, while status is Finalized or Reverted.","commonSituations":"Upstream verification logic short-circuits and skips recording decisions when no committee/vote records were fetched; a refactoring changed decision collection to a filtered iterator that yields nothing; a hand-constructed ExecutionFinalityTransition in tests or tooling forgot to populate decisions.","solutions":["Populate `finality.decisions` with the verification decision records produced by the finality verifier before calling the persistence API","Check upstream decision collection: ensure the verifier's output is not being filtered to empty (e.g. an over-strict predicate or a failed fetch of vote/decision data)","If a transaction genuinely has no decision evidence, route it through the non-verified finality recording path instead of record_execution_finality_verified"],"exampleFix":"// before\nlet finality = ExecutionFinalityTransition { status, decisions: &[], .. };\ndb.record_execution_finality_verified(&finality).await?;\n// after\nlet finality = ExecutionFinalityTransition { status, decisions: &decisions[..], .. };\nassert!(!decisions.is_empty(), \"finality must carry decision evidence\");\ndb.record_execution_finality_verified(&finality).await?;","handlingStrategy":"validation","validationCode":"// Rust: check before calling the persistence API\nif finality.decisions.is_empty() {\n    return Err(anyhow!(\"refusing to persist finality without decision evidence\"));\n}\ndb.record_execution_finality_verified(&finality).await?;","typeGuard":"fn has_decision_evidence(finality: &ExecutionFinalityTransition<'_>) -> bool {\n    !finality.decisions.is_empty()\n}","tryCatchPattern":"match db.record_execution_finality_verified(&finality).await {\n    Err(e) if e.to_string().contains(\"decision evidence\") => {\n        // rebuild decisions from the verifier, then retry once\n    }\n    other => other?,\n}","preventionTips":["Always construct ExecutionFinalityTransition from the verifier's full decision output, never a filtered subset by default","Add a debug_assert!(!decisions.is_empty()) at the construction site","Route finality transitions without evidence to a dedicated quarantine/audit path instead of the verified recorder"],"tags":["validation","database","invariant","rust"],"backgroundTag":"empty-required-field","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"}