hyperledger/fabric · critical

block header mismatch between the block we pulled and the bl

Error message

block header mismatch between the block we pulled and the block we joined with, sequence %d

What it means

The follower previously joined the channel with a join block (obtained via the Join REST API) and now compares the block it pulled from the ordering service at the same sequence. The protobuf headers are not equal (proto.Equal fails), meaning the service's chain diverges from the block the node joined with, so appending would create an inconsistent ledger and the pull is aborted.

Source

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

			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) {
				c.logger.Debugf("Pulled blocks from %d to %d, last block is config", firstBlockToPull, nextBlock.Header.Number)
				c.lastConfig = nextBlock
				if err := c.blockPullerFactory.UpdateVerifierFromConfigBlock(nextBlock); err != nil {
					return n, errors.WithMessagef(err, "failed to update verifier from last config,  block number: %d", nextBlock.Header.Number)
				}
				if updateEndpoints {
					endpoints, err := cluster.EndpointconfigFromConfigBlock(nextBlock, c.cryptoProvider)
					if err != nil {
						return n, errors.WithMessagef(err, "failed to extract endpoints from last config,  block number: %d", nextBlock.Header.Number)

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Regenerate the join block from the current, authoritative ordering service (fetch latest config block) and re-join the channel.
  2. Confirm the join block's channel ID and network generation match the cluster the orderer is pointing at.
  3. If a re-genesis occurred, reset the orderer's local ledger and join data, then onboard again from the new genesis.
  4. Investigate ordering-service membership/config changes between when the join block was captured and now (config block numbers must align).
Defensive patterns

Strategy: validation

Validate before calling

jb, _ := proto.Marshal(joinBlock.Header)
if pb, _ := proto.Marshal(pulledBlock.Header); !bytes.Equal(jb, pb) { return errors.New("join block diverges from service chain; regenerate join block") }

Try / catch

if err != nil && strings.Contains(err.Error(), "block header mismatch between the block we pulled and the block we joined with") { regenerate join block and re-join }

Prevention

When it happens

Trigger: pullUntilTarget: c.joinBlock != nil and its Header.Number equals the pulled nextBlock's number, but nextBlock.Header differs from c.joinBlock.Header — the ordering service serves a different block at the join sequence than the block supplied in the channel Join configuration.

Common situations: The join block was taken from a snapshot/fork different from the live ordering service's chain; the admin generated the join block from a stale config or wrong channel; ordering service network was re-genesis'd after the join block was created.

Related errors


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