datahaven-xyz/datahaven · error · Error
InvalidHeaderMerkleProof
InvalidHeaderMerkleProof
Error message
InvalidHeaderMerkleProof
What it means
The `finality_branch` fails to prove that `finalized_header`'s block root is the finalized checkpoint root recorded in the state of `attested_header`. `verify_update` hashes the finalized header (`hash_tree_root`) and runs `verify_merkle_branch` against `update.attested_header.state_root` using the finalized-root generalized index. A failure means the finalized header is not actually the one attested — inconsistent or forged update data.
Solutions
- Regenerate the full update (attested header, finalized header, finality branch) from a single beacon node response (`/eth/v1/beacon/light_client/updates`).
- Verify the finalized-root generalized index matches the fork version of the attested header slot.
- Confirm the SSZ `hash_tree_root` of the finalized header matches what a consensus client computes.
- Check chain/network identity to rule out cross-chain header mixing.
Example fix
// before let finalized = fetch_header(other_node, finalized_root); // different source // after let upd = client.get_light_client_update(slot); // attested, finalized, finality_branch all from the same response submit(upd.into());
Defensive patterns
Strategy: validation
Validate before calling
const root = hashTreeRoot(update.finalizedHeader);
if (!verifyMerkleBranch(root, update.finalityBranch, subtreeIndex(finalizedRootGindex), generalizedIndexLength(finalizedRootGindex), update.attestedHeader.stateRoot)) throw new Error('finality proof invalid'); Try / catch
try { await submit(update); } catch (e) { if (e.includes('InvalidHeaderMerkleProof')) { refetchUpdateFromSingleSource(); } else throw e; } Prevention
- Take attested+finalized headers and branch from one response
- Verify SSZ hash_tree_root parity with consensus clients
- Validate full update locally before extrinsic
When it happens
Trigger: Submitting an `Update` whose `finality_branch` does not verify: branch from a different attested state, finalized header hash-tree-root encoding mismatch, wrong `finalized_root_gindex` for the fork, or fields assembled from different sources.
Common situations: Relayer mixed headers from two beacon nodes or different slots; SSZ `hash_tree_root` implementation divergence after a fork; proof built with a pre-fork generalized index; malicious/buggy relayer submitting fabricated finalized headers.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- InvalidSyncCommitteeMerkleProof
- InvalidBlockRootsRootMerkleProof
- InvalidExecutionHeaderProof
- InvalidAncestryMerkleProof
- InvalidUpdateSlot
AI-assisted analysis of datahaven-xyz/datahaven@edcb13dbbc (2026-09-13).
Data as JSON: /api/errors/2378618b28a4d0e0.
Report an issue: GitHub.
Appendix: source
Thrown at operator/pallets/ethereum-client/src/lib.rs:382
latest_finalized_state
.slot
.saturating_add(config::SLOTS_PER_HISTORICAL_ROOT as u64)
>= update.finalized_header.slot,
Error::<T>::InvalidFinalizedHeaderGap
);
let fork_versions = T::ForkVersions::get();
let finalized_root_gindex = Self::finalized_root_gindex_at_slot(
update.attested_header.slot,
fork_versions.clone(),
);
// Verify that the `finality_branch`, if present, confirms `finalized_header` to match
// the finalized checkpoint root saved in the state of `attested_header`.
let finalized_block_root: H256 = update
.finalized_header
.hash_tree_root()
.map_err(|_| Error::<T>::HeaderHashTreeRootFailed)?;
ensure!(
verify_merkle_branch(
finalized_block_root,
&update.finality_branch,
subtree_index(finalized_root_gindex),
generalized_index_length(finalized_root_gindex),
update.attested_header.state_root
),
Error::<T>::InvalidHeaderMerkleProof
);
// Though following check does not belong to ALC spec we verify block_roots_root to
// match the finalized checkpoint root saved in the state of `finalized_header` so to
// cache it for later use in `verify_ancestry_proof`.
let block_roots_gindex = Self::block_roots_gindex_at_slot(
update.finalized_header.slot,
fork_versions.clone(),
);
ensure!(View on GitHub (pinned to edcb13dbbc)