hyperledger/fabric · error

block number should have been

Error message

block number should have been %d but was %d

What it means

addBlock enforces that the incoming block's Header.Number equals the current ledger height (next expected block number). If the block is a duplicate (number < height), out of order, or skipping ahead (number > height), it returns this error and the block is not committed.

Solutions

  1. Skip blocks with number less than the current ledger height (query GetBlockchainInfo first) instead of re-adding
  2. If height is behind, re-sync from a correct source starting exactly at bcInfo.Height
  3. Fix the block producer/orderer to avoid gaps; investigate ordering service for skipped numbers
  4. If the ledger is wrong/stuck, reset and re-sync the ledger rather than forcing mismatched blocks

Example fix

// before: blindly adds every delivered block
for _, blk := range blocks {
    if err := store.AddBlock(blk); err != nil { return err }
}
// after: skip already-committed blocks
bcInfo, _ := store.GetBlockchainInfo()
for _, blk := range blocks {
    if blk.Header.Number < bcInfo.Height { continue }
    if err := store.AddBlock(blk); err != nil { return err }
}
Defensive patterns

Strategy: validation

Validate before calling

bcInfo, err := store.GetBlockchainInfo()
if err != nil { return err }
if block.Header.Number < bcInfo.Height {
    return nil // duplicate: already committed, skip
}
if block.Header.Number > bcInfo.Height {
    return fmt.Errorf("gap: expected %d, got %d", bcInfo.Height, block.Header.Number)
}

Try / catch

if err := store.AddBlock(block); err != nil {
    if strings.Contains(err.Error(), "block number should have been") {
        // refresh bcInfo and skip/re-sync instead of failing the whole stream
    }
    return err
}

Prevention

When it happens

Trigger: Calling AddBlock with a block whose Header.Number != current height — re-committing an already-committed block, delivering blocks out of order, or gaps in the block sequence from a misbehaving deliverer.

Common situations: Deliver/gossip client reconnecting and replaying blocks already committed; a committer receiving a block range that overlaps local height after a ledger reset; ordering-service bugs producing block gaps; application code driving the ledger API directly with wrong numbering.

Related errors


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

Appendix: source

Thrown at common/ledger/blkstorage/blockfile_mgr.go:287

	nextFileWriter, err := newBlockfileWriter(
		deriveBlockfilePath(mgr.rootDir, blkfilesInfo.latestFileNumber),
	)
	if err != nil {
		panic(fmt.Sprintf("Could not open writer to next file: %s", err))
	}
	mgr.currentFileWriter.close()
	err = mgr.saveBlkfilesInfo(blkfilesInfo, true)
	if err != nil {
		panic(fmt.Sprintf("Could not save next block file info to db: %s", err))
	}
	mgr.currentFileWriter = nextFileWriter
	mgr.updateBlockfilesInfo(blkfilesInfo)
}

func (mgr *blockfileMgr) addBlock(block *common.Block) error {
	bcInfo := mgr.getBlockchainInfo()
	if block.Header.Number != bcInfo.Height {
		return errors.Errorf(
			"block number should have been %d but was %d",
			mgr.getBlockchainInfo().Height, block.Header.Number,
		)
	}

	// Add the previous hash check - Though, not essential but may not be a bad idea to
	// verify the field `block.Header.PreviousHash` present in the block.
	// This check is a simple bytes comparison and hence does not cause any observable performance penalty
	// and may help in detecting a rare scenario if there is any bug in the ordering service.
	if !bytes.Equal(block.Header.PreviousHash, bcInfo.CurrentBlockHash) {
		return errors.Errorf(
			"unexpected Previous block hash. Expected PreviousHash = [%x], PreviousHash referred in the latest block= [%x]",
			bcInfo.CurrentBlockHash, block.Header.PreviousHash,
		)
	}
	blockBytes, info := serializeBlock(block)
	blockHash := protoutil.BlockHeaderHash(block.Header)
	// Get the location / offset where each transaction starts in the block and where the block ends

View on GitHub (pinned to 2736b63f8f)