hyperledger/fabric · error

error reading [%d] bytes from file number [%d]

Error message

error reading [%d] bytes from file number [%d]

What it means

Wraps the io.ReadAtLeast error when reading the block body of `length` bytes after the size prefix was discarded. io.ReadAtLeast returns io.ErrUnexpectedEOF if EOF is hit before `length` bytes are read, or the underlying read error otherwise. The error is logged with Errorf before wrapping, and usually means the file holds less data than the block's declared size.

Source

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

		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,
		blockStartOffset: s.currentOffset,
		blockBytesOffset: s.currentOffset + int64(n),
	}
	s.currentOffset += int64(n) + int64(length)
	logger.Debugf("Returning blockbytes - length=[%d], placementInfo={%s}", len(blockBytes), blockPlacementInfo)
	return blockBytes, blockPlacementInfo, nil
}

func (s *blockfileStream) close() error {
	return errors.WithStack(s.file.Close())
}

// /////////////////////////////////
// blockStream functions
// //////////////////////////////////

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Handle it like a torn write: recover by truncating the file after the last complete block (this is how Fabric recovery treats it)
  2. Check disk space and filesystem fsync behavior on the ledger volume
  3. Never copy ledger files from a live peer; take a stopped-peer snapshot or use snapshot service
  4. If it recurs on a healthy file, verify storage integrity and restore from backup/re-sync

Example fix

// before
blockBytes, err := readBlock(stream)
if err != nil { return err }
// after
if _, err := readBlock(stream); err != nil {
    if errors.Is(errors.Cause(err), io.ErrUnexpectedEOF) {
        return recoverTornTail(file) // truncate after last complete block
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

info, err := os.Stat(blockfilePath)
if err != nil || info.Size() < expectedMinSize {
    return fmt.Errorf("file shorter than expected; torn write likely")
}

Type guard

func isShortRead(err error) bool {
    c := errors.Cause(err)
    return errors.Is(c, io.ErrUnexpectedEOF) || errors.Is(c, io.EOF)
}

Try / catch

blk, err := stream.nextBlockBytes()
if err != nil {
    if isShortRead(err) {
        logger.Warningf("torn block append detected: %v", err)
        return truncateAfterLastCompleteBlock(blockfilePath)
    }
    return err
}

Prevention

When it happens

Trigger: nextBlockBytesAndPlacementInfo: the varint prefix declared `length` bytes but io.ReadAtLeast(s.reader, blockBytes, length) hits EOF early — a torn append where the size prefix was written but the body was not fully flushed.

Common situations: Peer crash or power loss between writing the length prefix and the block body; disk-full cutting an append short; reading a blockfile copied mid-write from a running peer.

Related errors


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