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

  1. Guard callers: skip/flag empty keys returned by the iterator instead of decoding them.
  2. If seen in production, treat the index DB as corrupted and rebuild the block index.
  3. 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

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


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