hyperledger/fabric · critical

block header previous hash mismatch on sequence %d, expected

Error message

block header previous hash mismatch on sequence %d, expected %x, got %x

What it means

The follower pulled block `seq` from the ordering service and found its Header.PreviousHash does not equal the hash of the preceding block it verified. This is a hash-chain break between consecutively pulled blocks, so the follower refuses to append and returns ErrRetryCountExhausted-wrapped error. It protects against appending a block from a forked or tampered chain.

Source

Thrown at orderer/common/follower/follower_chain.go:541

		actualPrevHash = protoutil.BlockHeaderHash(prevBlock.Header)
	}

	// Pull until the latest height
	for seq := firstBlockToPull; seq < targetHeight; seq++ {
		n := seq - firstBlockToPull
		select {
		case <-c.stopChan:
			c.logger.Debug("Received a stop signal")
			return n, ErrChainStopped
		default:
			nextBlock := c.blockPuller.PullBlock(seq)
			if nextBlock == nil {
				return n, errors.WithMessagef(cluster.ErrRetryCountExhausted, "failed to pull block %d", seq)
			}

			reportedPrevHash := nextBlock.Header.PreviousHash
			if (nextBlock.Header.Number > 0) && !bytes.Equal(reportedPrevHash, actualPrevHash) {
				return n, errors.Errorf("block header previous hash mismatch on sequence %d, expected %x, got %x",
					nextBlock.Header.Number, actualPrevHash, reportedPrevHash)
			}

			if c.joinBlock != nil && c.joinBlock.Header.Number == nextBlock.Header.Number {
				// We don't need to verify the block.Data because we verify the join-block's DataHash against the
				// hash(join-block.Data) when we verify it during the `Join` REST API call
				if !proto.Equal(nextBlock.Header, c.joinBlock.Header) {
					c.logger.Errorf("Block header mismatch between the block we pulled and the block we joined with, sequence %d", c.joinBlock.Header.Number)
					return n, errors.Errorf("block header mismatch between the block we pulled and the block we joined with, sequence %d", c.joinBlock.Header.Number)
				}
			}

			actualPrevHash = protoutil.BlockHeaderHash(nextBlock.Header)
			if err := c.ledgerResources.Append(nextBlock); err != nil {
				return n, errors.WithMessagef(err, "failed to append block %d to the ledger", nextBlock.Header.Number)
			}

			if protoutil.IsConfigBlock(nextBlock) {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Verify the follower is connected to the correct, trusted ordering endpoints (orderer endpoint list / cluster root CAs in the join request) and the intended channel.
  2. Compare the offending block against other orderers' blocks; if forks are detected, investigate the consensus/raft state of the ordering service.
  3. Re-join the follower with a clean ledger and correct join block so verification restarts from a known-good genesis/config block.
  4. Enable TLS with proper root CAs to eliminate block corruption/MITM in transit, then retry pullUntilLatestWithRetry.
Defensive patterns

Strategy: try-catch

Try / catch

if err := pullUntilLatestWithRetry(...); err != nil { if strings.Contains(err.Error(), "previous hash mismatch") { quarantine follower; investigate ordering service fork } }

Prevention

When it happens

Trigger: pullUntilTarget pulls consecutive blocks and nextBlock.Header.Number > 0 with bytes.Equal(reportedPrevHash, actualPrevHash) false — the ordering service served a block whose PreviousHash does not match the previously appended block's hash (fork, tampering, or serving blocks from a different channel/consensus history).

Common situations: A malicious or misconfigured ordering node serving blocks from a divergent fork; a follower connected to the wrong ordering endpoint/channel; block bytes corrupted in transit without TLS/integrity protection; mixing snapshots from different network generations.

Related errors


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