{"record":{"id":"e29e57d3b8706c21","repo":"hyperledger/fabric","slug":"wasn-t-able-to-read-ledger-height","errorCode":null,"errorMessage":"wasn't able to read ledger height","messagePattern":"wasn't able to read ledger height","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gossip/privdata/dataretriever.go","lineNumber":56,"sourceCode":"}\n\n// NewDataRetriever constructing function for implementation of the\n// StorageDataRetriever interface\nfunc NewDataRetriever(channelID string, store *transientstore.Store, committer committer.Committer) StorageDataRetriever {\n\treturn &dataRetriever{\n\t\tlogger:    logger.With(\"channel\", channelID),\n\t\tstore:     store,\n\t\tcommitter: committer,\n\t}\n}\n\n// CollectionRWSet retrieves for give digest relevant private data if\n// available otherwise returns nil, bool which is true if data fetched from ledger and false if was fetched from transient store, and an error\nfunc (dr *dataRetriever) CollectionRWSet(digests []*protosgossip.PvtDataDigest, blockNum uint64) (Dig2PvtRWSetWithConfig, bool, error) {\n\theight, err := dr.committer.LedgerHeight()\n\tif err != nil {\n\t\t// if there is an error getting info from the ledger, we need to try to read from transient store\n\t\treturn nil, false, errors.Wrap(err, \"wasn't able to read ledger height\")\n\t}\n\n\t// The condition may be true for either commit or reconciliation case when another peer sends a request to retrieve private data.\n\t// For the commit case, get the private data from the transient store because the block has not been committed.\n\t// For the reconciliation case, this peer is further behind the ledger height than the peer that requested for the private data.\n\t// In this case, the ledger does not have the requested private data. Also, the data cannot be queried in the transient store,\n\t// as the txID in the digest will be missing.\n\tif height <= blockNum { // Check whenever current ledger height is equal or below block sequence num.\n\t\tdr.logger.Debug(\"Current ledger height \", height, \"is below requested block sequence number\",\n\t\t\tblockNum, \"retrieving private data from transient store\")\n\n\t\tresults := make(Dig2PvtRWSetWithConfig)\n\t\tfor _, dig := range digests {\n\t\t\t// skip retrieving from transient store if txid is not available\n\t\t\tif dig.TxId == \"\" {\n\t\t\t\tdr.logger.Infof(\"Skip querying transient store for chaincode %s, collection name %s, block number %d, sequence in block %d, \"+\n\t\t\t\t\t\"as the txid is missing, perhaps because it is a reconciliation request\",\n\t\t\t\t\tdig.Namespace, dig.Collection, blockNum, dig.SeqInBlock)","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/gossip/privdata/dataretriever.go#L38-L74","documentation":"dataRetriever.CollectionRWSet first queries the committer's LedgerHeight() to decide whether private data should come from the transient store (block not yet committed) or the ledger (reconciliation). If the ledger read fails for any reason, the error is wrapped with this message and returned. The retriever then falls back to reading the transient store.","triggerScenarios":"dr.committer.LedgerHeight() returns an error — e.g. ledger DB not open, underlying LevelDB/peer errors, or the peer still initializing its ledger when a gossip private-data pull request arrives.","commonSituations":"Peer restarting while gossip requests arrive; ledger/state database corruption; disk full preventing ledger reads; peer joining a channel whose ledger bootstrap hasn't finished.","solutions":["Check peer logs for the underlying wrapped error (io, leveldb, closed store) and fix that root cause first.","Restart the peer with `peer node start` so the ledger is fully opened before gossip handles requests.","Run `peer node rebuild-dbs` if the ledger/state DB is corrupted.","Ensure sufficient disk space and healthy storage for the peer's ledger directories."],"exampleFix":"// before: treating the error as fatal for the pull\nreturn nil, false, errors.Wrap(err, \"wasn't able to read ledger height\")\n\n// after: caller falls back to transient store only\nif _, _, err := dr.CollectionRWSet(digests, blockNum); err != nil {\n    logger.Warningf(\"falling back to transient store after ledger error: %v\", err)\n}","handlingStrategy":"fallback","validationCode":"height, err := committer.LedgerHeight()\nif err != nil {\n    logger.Warningf(\"ledger height unavailable, deferring pull: %v\", err)\n    return\n}","typeGuard":"func ledgerReady(c privdata.Committer) bool {\n    _, err := c.LedgerHeight()\n    return err == nil\n}","tryCatchPattern":"dig2PvtRWSet, fromLedger, err := dr.CollectionRWSet(digests, blockNum)\nif err != nil {\n    if strings.Contains(err.Error(), \"wasn't able to read ledger height\") {\n        // transient store fallback is inherent; queue a retry\n        scheduleRetry(digests)\n        return\n    }\n    logger.Errorf(\"pull failed: %v\", err)\n}","preventionTips":["Ensure the peer's ledger is fully opened before gossip starts serving pulls.","Monitor disk space for ledger directories.","Restart peers cleanly; watch for leveldb errors at startup.","Gate reconciliation start on ledger readiness/health checks."],"tags":["fabric","ledger","gossip","private-data"],"backgroundTag":"ledger-height-unavailable","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"}