hyperledger/fabric · error

expected block number=%d, received block number=%d

Error message

expected block number=%d, received block number=%d

What it means

The private data store Commit() checks that the block being committed matches its internal expected block number (nextBlockNum). If the caller supplies a block number that is not exactly the next sequential one, the store rejects it to keep private-data key ordering consistent with the block store. This indicates out-of-order or duplicate block commits reaching the pvtdata store.

Source

Thrown at core/ledger/pvtdatastorage/store.go:316

		s.isLastUpdatedOldBlocksSet = true
	} // false if not set

	return nil
}

// Init initializes the store. This function is expected to be invoked before using the store
func (s *Store) Init(btlPolicy pvtdatapolicy.BTLPolicy) {
	s.btlPolicy = btlPolicy
}

// Commit commits the pvt data as well as both the eligible and ineligible
// missing private data --- `eligible` denotes that the missing private data belongs to a collection
// for which this peer is a member; `ineligible` denotes that the missing private data belong to a
// collection for which this peer is not a member.
func (s *Store) Commit(blockNum uint64, pvtData []*ledger.TxPvtData, missingPvtData ledger.TxMissingPvtData, purgeMarkers []*PurgeMarker) error {
	expectedBlockNum := s.nextBlockNum()
	if expectedBlockNum != blockNum {
		return errors.Errorf("expected block number=%d, received block number=%d", expectedBlockNum, blockNum)
	}

	batch := s.db.NewUpdateBatch()
	var err error
	var key, val []byte

	storeEntries, err := prepareStoreEntries(blockNum, pvtData, s.btlPolicy, missingPvtData, purgeMarkers)
	if err != nil {
		return err
	}

	for _, dataEntry := range storeEntries.dataEntries {
		key = encodeDataKey(dataEntry.key)
		if val, err = encodeDataValue(dataEntry.value); err != nil {
			return err
		}
		batch.Put(key, val)
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Verify blocks are committed strictly in sequence; check the caller (commitToPvtAndBlockStore) is not skipping or duplicating block numbers.
  2. If the stores diverged, reset both the block store and pvtdata store to the same height (e.g. peer node reset/rollback to a common block).
  3. Check for stale backups: restore block store and pvtdata store snapshots taken at the same block height.
  4. Inspect logs for the expected vs received numbers to determine which store is ahead/behind and resync accordingly.

Example fix

// before
pvtdataStore.Commit(ledger.NextBlockNumber(currentBlockNum)+1, pvtData, missing, purgeMarkers)
// after
pvtdataStore.Commit(nextExpectedBlockNum, pvtData, missing, purgeMarkers) // must equal store.nextBlockNum()
Defensive patterns

Strategy: validation

Validate before calling

if blockNum != expectedNextBlockNum(store) {
    return fmt.Errorf("refusing commit: block %d out of sequence, expected %d", blockNum, expectedNextBlockNum(store))
}
err := store.Commit(blockNum, pvtData, missing, purgeMarkers)

Try / catch

if err := store.Commit(blockNum, pvtData, missing, purgeMarkers); err != nil {
    if strings.Contains(err.Error(), "expected block number") {
        // sequence divergence: halt commit pipeline, run ledger reset/rollback to resync
    }
    return err
}

Prevention

When it happens

Trigger: Calling Store.Commit(blockNum, ...) with a blockNum != s.nextBlockNum() — e.g. committing block N+1 before N, re-committing an already-committed block, or resetting one store but not the other so the sequences diverge.

Common situations: Ledger rollback/reset applied to the block store but not the private data store (or vice versa); custom ledger implementations committing out of order; concurrent/gossip-driven commit races; restoring from a backup that is offset by one or more blocks.

Related errors


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