hyperledger/fabric · critical

Error in decoding varint bytes [%#v]

Error message

Error in decoding varint bytes [%#v]

What it means

A panic raised when protowire.ConsumeVarint returns n <= 0 on the peeked length-prefix bytes while moreContentAvailable is still true. This means the bytes at the current offset are not a valid varint at all — corruption, not a torn tail (a torn tail with no more content returns ErrUnexpectedEndOfBlockfile instead). The code cannot make any progress, so it panics.

Source

Thrown at common/ledger/blkstorage/block_stream.go:115

	// Peek 8 or smaller number of bytes (if remaining bytes are less than 8)
	// Assumption is that a block size would be small enough to be represented in 8 bytes varint
	peekBytes := 8
	if remainingBytes < int64(peekBytes) {
		peekBytes = int(remainingBytes)
		moreContentAvailable = false
	}
	logger.Debugf("Remaining bytes=[%d], Going to peek [%d] bytes", remainingBytes, peekBytes)
	if lenBytes, err = s.reader.Peek(peekBytes); err != nil {
		return nil, nil, errors.Wrapf(err, "error peeking [%d] bytes from block file", peekBytes)
	}
	length, n := protowire.ConsumeVarint(lenBytes)
	if n <= 0 {
		// proto.DecodeVarint did not consume any byte at all which means that the bytes
		// representing the size of the block are partial bytes
		if !moreContentAvailable {
			return nil, nil, ErrUnexpectedEndOfBlockfile
		}
		panic(errors.Errorf("Error in decoding varint bytes [%#v]", lenBytes))
	}
	bytesExpected := int64(n) + int64(length)
	if bytesExpected > remainingBytes {
		logger.Debugf("At least [%d] bytes expected. Remaining bytes = [%d]. Returning with error [%s]",
			bytesExpected, remainingBytes, ErrUnexpectedEndOfBlockfile)
		return nil, nil, ErrUnexpectedEndOfBlockfile
	}
	// skip the bytes representing the block size
	if _, err = s.reader.Discard(n); err != nil {
		return nil, nil, errors.Wrapf(err, "error discarding [%d] bytes", n)
	}
	blockBytes := make([]byte, length)
	if _, err = io.ReadAtLeast(s.reader, blockBytes, int(length)); err != nil {
		logger.Errorf("Error reading [%d] bytes from file number [%d], error: %s", length, s.fileNum, err)
		return nil, nil, errors.Wrapf(err, "error reading [%d] bytes from file number [%d]", length, s.fileNum)
	}
	blockPlacementInfo := &blockPlacementInfo{
		fileNum:          s.fileNum,

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Treat as file corruption: run ledger integrity checks and restore the blockfile from backup or re-sync from other peers/orderers
  2. Verify no external process wrote to the ledger directory
  3. Check the offset source — a corrupt checkpoint can point mid-block; rebuild the checkpoint/rollback
  4. Check storage health (SMART, dmesg) for bit rot
Defensive patterns

Strategy: validation

Validate before calling

func looksLikeVarintPrefix(b []byte) bool {
    _, n := protowire.ConsumeVarint(b)
    return n > 0
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        logger.Errorf("blockfile corruption: %v", r)
        triggerLedgerRebuild()
    }
}()

Prevention

When it happens

Trigger: nextBlockBytesAndPlacementInfo peeks bytes, ConsumeVarint fails (n<=0), AND moreContentAvailable==true — i.e. remaining bytes >= peekBytes but they do not decode as a varint length prefix.

Common situations: Bit rot / disk corruption of the blockfile; writes to the file from an unexpected process; reading a non-blockfile or a file with a wrong offset (corrupt checkpoint); manually edited or patched ledger files.

Related errors


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