{"record":{"id":"1e38a5c75d271879","repo":"nautechsystems/nautilus_trader","slug":"decision-header-changed-before-signing","errorCode":null,"errorMessage":"Decision header changed before signing","messagePattern":"Decision header changed before signing","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/blockchain/src/execution/client.rs","lineNumber":2496,"sourceCode":"            tx_hash,\n            raw_tx,\n            payload_lease: Some(payload_lease),\n        })\n    }\n\n    async fn verify_pre_sign_header_fence(\n        &self,\n        target: VerifiedBlockHeader,\n    ) -> anyhow::Result<Vec<ExecutionVerificationDecision>> {\n        let checkpoint = required_verification(\n            self.verification.verify_checkpoint().await,\n            \"pre-sign checkpoint reread\",\n        )?;\n        let header = required_verification(\n            self.verification.verify_block(target.number).await,\n            \"pre-sign decision header reread\",\n        )?;\n        anyhow::ensure!(\n            header.value == target,\n            \"Decision header changed before signing\"\n        );\n        Ok(vec![\n            verification_decision(\n                &checkpoint,\n                Some(checkpoint.value.number),\n                Some(checkpoint.value.number),\n            ),\n            verification_decision(&header, Some(target.number), Some(target.number)),\n        ])\n    }\n\n    async fn verify_decision_ancestry(\n        &self,\n        target: VerifiedBlockHeader,\n    ) -> anyhow::Result<Vec<ExecutionVerificationDecision>> {\n        let checkpoint = required_verification(","sourceCodeStart":2478,"sourceCodeEnd":2514,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/blockchain/src/execution/client.rs#L2478-L2514","documentation":"As a final fence before signing, `verify_pre_sign_header_fence` re-verifies the block header at the decision block's height and requires it to equal the header verified earlier in the flow. If the header at that height changed (reorg or a provider returning different data), the decision state is no longer trustworthy and the client refuses to sign.","triggerScenarios":"Calling the pre-sign path and, between the initial `verify_decision_header`/`verify_block` call and the fence re-read, the block at `target.number` is replaced by a different block (reorg) or the verification provider returns a differing header for the same height.","commonSituations":"Operating on a chain with short finality and fast block production where reorgs occur during the verification window; RPC load balancer serving inconsistent nodes; deep reorg after an upstream client issue.","solutions":["Retry the transaction preparation; the fence will pass if the header is stable on the second attempt.","Anchor decisions to finalized (not just latest) blocks so a reorg cannot swap the header before signing.","Pin the client to a single consistent RPC node rather than a load-balanced set.","Investigate chain health if this recurs — repeated mismatches indicate unstable finality or an unreliable verification source."],"exampleFix":"// before: decision header taken from latest (unfinalized)\nlet header = verification.verify_block(latest_number).await;\n// after: use a finalized header for the decision\nlet header = verification.verify_block(finalized_number).await;","handlingStrategy":"retry","validationCode":"pub async fn header_stable(ver: &Verification, number: u64) -> anyhow::Result<bool> {\n    let a = ver.verify_block(number).await?;\n    tokio::time::sleep(Duration::from_millis(250)).await;\n    let b = ver.verify_block(number).await?;\n    Ok(a.value == b.value)\n}","typeGuard":null,"tryCatchPattern":"match prepare().await {\n    Err(e) if e.to_string().contains(\"Decision header changed before signing\") => {\n        backoff_retry(prepare, 3).await?\n    }\n    other => other?,\n}","preventionTips":["Anchor decisions to finalized blocks rather than latest.","Pin a single consistent RPC node instead of a load-balanced pool.","Alert on repeated fence failures — they indicate reorgs or provider inconsistency."],"tags":["blockchain","reorg","consistency-check","pre-sign"],"backgroundTag":"checksum-mismatch","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"}