hyperledger/fabric · error
previous header hash is %s but expected %s
Error message
previous header hash is %s but expected %s
What it means
verifyHashChainAndDataHash compares the proposed block's Header.PreviousHash with the hash of the previous block that the verifier expects (prevHeaderHash). A mismatch means the proposed block does not continue the chain, so the proposal is invalid.
Source
Thrown at orderer/consensus/smartbft/verifier.go:248
expectedMsgToBeSigned := util.ConcatenateBytes(sig.OrdererBlockMetadata, sig.IdentifierHeader, sig.BlockHeader, nil)
signedData := &protoutil.SignedData{
Signature: signature.Value,
Data: expectedMsgToBeSigned,
Identity: identity,
}
return nil, v.ConsenterVerifier.Evaluate([]*protoutil.SignedData{signedData})
}
// VerificationSequence returns verification sequence
func (v *Verifier) VerificationSequence() uint64 {
return v.VerificationSequencer.Sequence()
}
func verifyHashChainAndDataHash(block *cb.Block, prevHeaderHash string) error {
thisHdrHashOfPrevHdr := hex.EncodeToString(block.Header.PreviousHash)
if prevHeaderHash != thisHdrHashOfPrevHdr {
return errors.Errorf("previous header hash is %s but expected %s", thisHdrHashOfPrevHdr, prevHeaderHash)
}
dataHash, err := protoutil.BlockDataHash(block.Data)
if err != nil {
return err
}
dataHashString := hex.EncodeToString(block.Header.DataHash)
actualHashOfData := hex.EncodeToString(dataHash)
if dataHashString != actualHashOfData {
return errors.Errorf("data hash is %s but expected %s", dataHashString, actualHashOfData)
}
return nil
}
func (v *Verifier) verifyBlockDataAndMetadata(block *cb.Block, metadata []byte) ([]types.RequestInfo, error) {
if block.Data == nil || len(block.Data.Data) == 0 {
return nil, errors.New("empty block data")View on GitHub (pinned to 2736b63f8f)
Solutions
- Ensure the proposing node has the latest committed block before building the proposal
- Force re-sync of the diverged node's ledger and re-propose
- Check for split-brain / multiple leaders after view change; confirm the leader is the current one
- Compare the block number and previous-hash of proposal vs local ledger to spot divergence
Defensive patterns
Strategy: validation
Validate before calling
prevHdrHash := hex.EncodeToString(sha256Sum(lastCommittedHeader))
if hex.EncodeToString(block.Header.PreviousHash) != prevHdrHash {
return errors.New("proposal does not extend the current chain; re-sync leader")
} Try / catch
if err := verifyHashChainAndDataHash(block, expectedPrevHash); err != nil && strings.Contains(err.Error(), "previous header hash") {
logger.Warn("stale/divergent proposal; requesting fresh proposal from leader")
} Prevention
- Leaders must sync to the latest committed block before proposing
- Monitor ledger heights across nodes to detect divergence early
- Ensure view-change logic elects a leader with the latest ledger
- Compare proposal sequence and previous hash before verification
When it happens
Trigger: VerifyProposal calls verifyHashChainAndDataHash with a block whose PreviousHash differs from hex-encoded hash of the prior block header, e.g. a proposal built on a stale or different chain.
Common situations: A node proposing while behind on blocks or diverged after a view change; competing proposals from different parents; a node resynchronizing after a crash with an older local ledger; epoch/sequence arithmetic errors after config change.
Related errors
- Header.PreviousHash of block [%d] is different from Hash(blo
- pending config does not match calculated expected config
- without a system channel, a follower should have been create
- failed parsing smartbft configuration
- failed creating a new BFTChain
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/075e684cf1b9d6c8.
Report an issue: GitHub.