stride3d/stride · error · IOException

Can't read beyond end of stream.

Error message

Can't read beyond end of stream.

What it means

AcquireNextChunk validates chunk metadata: compressedLength must not exceed originalLength, since LZ4 block compression never expands data. A chunk claiming a compressed size larger than its original size indicates a corrupted or malformed stream, so the library throws IOException.

Solutions

  1. Regenerate/re-download the compressed file; treat it as corrupted
  2. Verify the chunk header layout matches the LZ4Stream format (flags, originalLength, compressedLength order)
  3. Add a checksum/integrity check on stored compressed files
  4. Ensure no seeking/offset misalignment when wrapping the inner stream

Example fix

// before
using var s = new LZ4Stream(File.OpenRead(path)); // throws on corrupt header
// after
if (!HasValidChecksum(path)) throw new InvalidDataException("corrupt lz4 file");
using var s = new LZ4Stream(File.OpenRead(path));
Defensive patterns

Strategy: validation

Validate before calling

// before opening: verify integrity of stored file
if (!VerifyChecksum(path)) throw new InvalidDataException("LZ4 asset failed integrity check");

Try / catch

try { using var s = new LZ4Stream(File.OpenRead(path)); ... }
catch (IOException ex) when (ex.Message.Contains("beyond end of stream")) { // corrupted stream: redownload/regenerate
}

Prevention

When it happens

Trigger: Reading an LZ4Stream whose chunk header was corrupted (bit flips, bad write) so compressedLength > originalLength; reading a file produced by an incompatible writer.

Common situations: Disk corruption or partial overwrite of compressed assets; hand-rolled producers writing chunk headers incorrectly; concatenating streams incorrectly so the reader lands on garbage bytes.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/dc8379ec1d4a1a15. Report an issue: GitHub.

Appendix: source

Thrown at sources/core/Stride.Core.Serialization/Serialization/LZ4/LZ4Stream.cs:278

        innerStreamPosition += compressedLength;

        bufferOffset = 0;
    }

    /// <summary>Reads the next chunk from stream.</summary>
    /// <returns><c>true</c> if next has been read, or <c>false</c> if it is legitimate end of file.</returns>
    /// <exception cref="IOException">The end of the stream was unexpectedly reached.</exception>
    private bool AcquireNextChunk()
    {
        do
        {
            if (!TryReadVarInt(out var varint)) return false;
            var flags = (ChunkFlags)varint;
            var isCompressed = (flags & ChunkFlags.Compressed) != 0;

            var originalLength = (int)ReadVarInt();
            var compressedLength = isCompressed ? (int)ReadVarInt() : originalLength;
            if (compressedLength > originalLength) throw new IOException("Can't read beyond end of stream."); // corrupted

            if (compressedDataBuffer == null || compressedDataBuffer.Length < compressedLength)
                compressedDataBuffer = new byte[compressedLength];
            var chunk = ReadBlock(compressedDataBuffer, 0, compressedLength);

            if (chunk != compressedLength) throw new IOException("Can't read beyond end of stream."); // currupted

            if (!isCompressed)
            {
                // swap the buffers
                (compressedDataBuffer, dataBuffer) = (dataBuffer, compressedDataBuffer);
                bufferLength = compressedLength;
            }
            else
            {
                if (dataBuffer == null || dataBuffer.Length < originalLength)
                    dataBuffer = new byte[originalLength];
                var passes = (int)flags >> 2;

View on GitHub (pinned to 96fad776d2)