{"record":{"id":"ad4466db1baa4a0e","repo":"hyperledger/fabric","slug":"last-committed-block-number-d-smaller-than-the","errorCode":null,"errorMessage":"last committed block number [%d] smaller than the requested block number [%d]","messagePattern":"last committed block number \\[(.+?)\\] smaller than the requested block number \\[(.+?)\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/ledger/pvtdatastorage/store.go","lineNumber":481,"sourceCode":"\tbatch.Delete(lastUpdatedOldBlocksKey)\n\tif err := s.db.WriteBatch(batch, true); err != nil {\n\t\treturn err\n\t}\n\ts.isLastUpdatedOldBlocksSet = false\n\treturn nil\n}\n\n// GetPvtDataByBlockNum returns only the pvt data  corresponding to the given block number\n// The pvt data is filtered by the list of 'ns/collections' supplied in the filter\n// A nil filter does not filter any results\nfunc (s *Store) GetPvtDataByBlockNum(blockNum uint64, filter ledger.PvtNsCollFilter) ([]*ledger.TxPvtData, error) {\n\tlogger.Debugf(\"Get private data for block [%d], filter=%#v\", blockNum, filter)\n\tif s.isEmpty {\n\t\treturn nil, errors.New(\"the store is empty\")\n\t}\n\tlastCommittedBlock := atomic.LoadUint64(&s.lastCommittedBlock)\n\tif blockNum > lastCommittedBlock {\n\t\treturn nil, errors.Errorf(\"last committed block number [%d] smaller than the requested block number [%d]\", lastCommittedBlock, blockNum)\n\t}\n\tstartKey, endKey := getDataKeysForRangeScanByBlockNum(blockNum)\n\tlogger.Debugf(\"Querying private data storage for write sets using startKey=%#v, endKey=%#v\", startKey, endKey)\n\titr, err := s.db.GetIterator(startKey, endKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer itr.Release()\n\n\tvar blockPvtdata []*ledger.TxPvtData\n\tvar currentTxNum uint64\n\tvar currentTxWsetAssember *txPvtdataAssembler\n\tfirstItr := true\n\n\tfor itr.Next() {\n\t\tdataKeyBytes := itr.Key()\n\t\tdataValueBytes := itr.Value()\n\t\tdataKey, err := decodeDatakey(dataKeyBytes)","sourceCodeStart":463,"sourceCodeEnd":499,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/ledger/pvtdatastorage/store.go#L463-L499","documentation":"GetPvtDataByBlockNum rejects queries for blocks beyond the last committed block (atomic.LoadUint64(&s.lastCommittedBlock)). Private data for future blocks cannot exist yet, so the store returns this error instead of empty results. The message includes both the committed height and the requested block.","triggerScenarios":"Calling GetPvtDataByBlockNum(blockNum, ...) with blockNum > lastCommittedBlock — e.g. querying the block being validated/committed concurrently, or a stale client asking for a future block after a peer reset lowered the height.","commonSituations":"Clients racing ahead of peer commit during event handling; peer rollback/reset while an upstream service still queries old high block numbers; test harnesses that forget the store height is 0.","solutions":["Get the store/ledger height first and only query blocks <= lastCommittedBlock.","If a peer rollback/reset happened, re-query from the new height or resync the peer.","Retry with backoff if the block is expected to arrive shortly (event-driven consumers).","In tests, commit the target block (or seed lastCommittedBlock) before querying."],"exampleFix":"// before\npvt, err := store.GetPvtDataByBlockNum(blockNum, nil)\n// after\nlast := store.LastCommittedBlock()\nif blockNum > last {\n    return nil, fmt.Errorf(\"block %d not yet committed (height=%d)\", blockNum, last)\n}\npvt, err := store.GetPvtDataByBlockNum(blockNum, nil)","handlingStrategy":"validation","validationCode":"last := atomic.LoadUint64(&storeLastCommittedBlock) // or query ledger height\nif blockNum > last {\n    return nil, fmt.Errorf(\"block %d exceeds committed height %d\", blockNum, last)\n}\npvt, err := store.GetPvtDataByBlockNum(blockNum, filter)","typeGuard":null,"tryCatchPattern":"pvt, err := store.GetPvtDataByBlockNum(blockNum, filter)\nif err != nil {\n    if strings.Contains(err.Error(), \"smaller than the requested block number\") {\n        // retry with backoff: block may still be in flight\n        return retryWithBackoff(func() ([]*ledger.TxPvtData, error) {\n            return store.GetPvtDataByBlockNum(blockNum, filter)\n        })\n    }\n    return nil, err\n}","preventionTips":["Always derive the max queryable block from ledger/store height, not client assumptions.","After peer reset/rollback, clamp or recompute stored high-water marks in consumers.","Use block events to advance your query cursor instead of polling arbitrary heights."],"tags":["ledger","private-data","stale-read"],"backgroundTag":"block-number-beyond-ledger-height","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}