hyperledger/fabric · error
invalid txIDKey - zero-length slice
Error message
invalid txIDKey - zero-length slice
What it means
retrieveTxID decodes index keys of the format prefix:len(TxID):TxID:BlkNum:TxNum. If the input slice is empty, there is nothing to decode, so the function returns this error. Callers pass raw keys from the txID index iterator, so a zero-length key indicates a corrupted or malformed iterator position.
Source
Thrown at common/ledger/blkstorage/blockindex.go:429
func constructBlockHashKey(blockHash []byte) []byte {
return append([]byte{blockHashIdxKeyPrefix}, blockHash...)
}
func constructTxIDKey(txID string, blkNum, txNum uint64) []byte {
k := append(
[]byte{txIDIdxKeyPrefix},
util.EncodeOrderPreservingVarUint64(uint64(len(txID)))...,
)
k = append(k, txID...)
k = append(k, util.EncodeOrderPreservingVarUint64(blkNum)...)
return append(k, util.EncodeOrderPreservingVarUint64(txNum)...)
}
// retrieveTxID takes input an encoded txid key of the format `prefix:len(TxID):TxID:BlkNum:TxNum`
// and returns the TxID from this
func retrieveTxID(encodedTxIDKey []byte) (string, error) {
if len(encodedTxIDKey) == 0 {
return "", errors.New("invalid txIDKey - zero-length slice")
}
if encodedTxIDKey[0] != txIDIdxKeyPrefix {
return "", errors.Errorf("invalid txIDKey {%x} - unexpected prefix", encodedTxIDKey)
}
remainingBytes := encodedTxIDKey[utf8.RuneLen(txIDIdxKeyPrefix):]
txIDLen, n, err := util.DecodeOrderPreservingVarUint64(remainingBytes)
if err != nil {
return "", errors.WithMessagef(err, "invalid txIDKey {%x}", encodedTxIDKey)
}
remainingBytes = remainingBytes[n:]
if len(remainingBytes) <= int(txIDLen) {
return "", errors.Errorf("invalid txIDKey {%x}, fewer bytes present", encodedTxIDKey)
}
return string(remainingBytes[:int(txIDLen)]), nil
}
func retrieveBlockNum(encodedTxIDKey []byte, BlkNumStartingIndex int) (uint64, error) {View on GitHub (pinned to 2736b63f8f)
Solutions
- Guard callers: skip/flag empty keys returned by the iterator instead of decoding them.
- If seen in production, treat the index DB as corrupted and rebuild the block index.
- In code, check len(key) > 0 before calling retrieveTxID.
Example fix
// before
txID, err := retrieveTxID(dbItr.Key())
// after
if len(dbItr.Key()) == 0 {
continue // skip empty key
}
txID, err := retrieveTxID(dbItr.Key()) Defensive patterns
Strategy: validation
Validate before calling
if len(encodedTxIDKey) == 0 {
return "", errors.New("skipping empty txID key")
} Type guard
func isValidTxIDKey(k []byte) bool {
return len(k) > 0 && k[0] == txIDIdxKeyPrefix
} Try / catch
txID, err := retrieveTxID(key)
if err != nil && strings.Contains(err.Error(), "zero-length slice") {
// log & skip malformed key during iteration
continue
}
if err != nil { return err } Prevention
- Always skip or quarantine empty iterator keys instead of decoding them blindly.
- If empty keys appear in production, rebuild the index — it indicates DB corruption.
When it happens
Trigger: exportUniqueTxIDs feeding dbItr.Key() when the iterator yields an empty key, or unit-test calls (TestTxIDKeyDecodingInvalidInputs) passing empty byte slices directly.
Common situations: Corrupted leveldb index content yielding empty keys during iteration; tests validating decoder robustness.
Related errors
- invalid txIDKey {%x} - unexpected prefix
- invalid txIDKey {%x}, fewer bytes present
- error decoding the block number
- error decoding the data hash
- error decoding the previous hash
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/0c38e6dff2b2adde.
Report an issue: GitHub.