hyperledger/fabric · error

wasn't able to read ledger height

Error message

wasn't able to read ledger height

What it means

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.

Source

Thrown at gossip/privdata/dataretriever.go:56

}

// NewDataRetriever constructing function for implementation of the
// StorageDataRetriever interface
func NewDataRetriever(channelID string, store *transientstore.Store, committer committer.Committer) StorageDataRetriever {
	return &dataRetriever{
		logger:    logger.With("channel", channelID),
		store:     store,
		committer: committer,
	}
}

// CollectionRWSet retrieves for give digest relevant private data if
// available otherwise returns nil, bool which is true if data fetched from ledger and false if was fetched from transient store, and an error
func (dr *dataRetriever) CollectionRWSet(digests []*protosgossip.PvtDataDigest, blockNum uint64) (Dig2PvtRWSetWithConfig, bool, error) {
	height, err := dr.committer.LedgerHeight()
	if err != nil {
		// if there is an error getting info from the ledger, we need to try to read from transient store
		return nil, false, errors.Wrap(err, "wasn't able to read ledger height")
	}

	// The condition may be true for either commit or reconciliation case when another peer sends a request to retrieve private data.
	// For the commit case, get the private data from the transient store because the block has not been committed.
	// For the reconciliation case, this peer is further behind the ledger height than the peer that requested for the private data.
	// In this case, the ledger does not have the requested private data. Also, the data cannot be queried in the transient store,
	// as the txID in the digest will be missing.
	if height <= blockNum { // Check whenever current ledger height is equal or below block sequence num.
		dr.logger.Debug("Current ledger height ", height, "is below requested block sequence number",
			blockNum, "retrieving private data from transient store")

		results := make(Dig2PvtRWSetWithConfig)
		for _, dig := range digests {
			// skip retrieving from transient store if txid is not available
			if dig.TxId == "" {
				dr.logger.Infof("Skip querying transient store for chaincode %s, collection name %s, block number %d, sequence in block %d, "+
					"as the txid is missing, perhaps because it is a reconciliation request",
					dig.Namespace, dig.Collection, blockNum, dig.SeqInBlock)

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Check peer logs for the underlying wrapped error (io, leveldb, closed store) and fix that root cause first.
  2. Restart the peer with `peer node start` so the ledger is fully opened before gossip handles requests.
  3. Run `peer node rebuild-dbs` if the ledger/state DB is corrupted.
  4. Ensure sufficient disk space and healthy storage for the peer's ledger directories.

Example fix

// before: treating the error as fatal for the pull
return nil, false, errors.Wrap(err, "wasn't able to read ledger height")

// after: caller falls back to transient store only
if _, _, err := dr.CollectionRWSet(digests, blockNum); err != nil {
    logger.Warningf("falling back to transient store after ledger error: %v", err)
}
Defensive patterns

Strategy: fallback

Validate before calling

height, err := committer.LedgerHeight()
if err != nil {
    logger.Warningf("ledger height unavailable, deferring pull: %v", err)
    return
}

Type guard

func ledgerReady(c privdata.Committer) bool {
    _, err := c.LedgerHeight()
    return err == nil
}

Try / catch

dig2PvtRWSet, fromLedger, err := dr.CollectionRWSet(digests, blockNum)
if err != nil {
    if strings.Contains(err.Error(), "wasn't able to read ledger height") {
        // transient store fallback is inherent; queue a retry
        scheduleRetry(digests)
        return
    }
    logger.Errorf("pull failed: %v", err)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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