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

  1. Ensure the proposing node has the latest committed block before building the proposal
  2. Force re-sync of the diverged node's ledger and re-propose
  3. Check for split-brain / multiple leaders after view change; confirm the leader is the current one
  4. 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

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


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/075e684cf1b9d6c8. Report an issue: GitHub.