Skip to content

Fix DataErrorException when extracting LZMA-compressed zero-byte ZIP entries#1237

Merged
adamhathcock merged 3 commits intomasterfrom
copilot/fix-zip-extraction-error
Mar 3, 2026
Merged

Fix DataErrorException when extracting LZMA-compressed zero-byte ZIP entries#1237
adamhathcock merged 3 commits intomasterfrom
copilot/fix-zip-extraction-error

Conversation

Copy link
Contributor

Copilot AI commented Feb 27, 2026

Extracting a ZIP archive containing an LZMA-compressed entry with 0 uncompressed bytes throws DataErrorException from LzmaStream.Read().

Root cause

LZMA entries in ZIP always carry a 4-byte header (version + property length) plus 5-byte properties, followed by actual LZMA stream data — even for empty files. So CompressedSize > 0 while UncompressedSize == 0.

LzmaStream is constructed with outputSize=0, setting _availableBytes=0. The first Read() call immediately sets _endReached=true without running the decoder, then the end-of-stream check throws because _inputPosition (0, never incremented) ≠ _inputSize (> 0).

Fix

In ZipFilePart.CreateDecompressionStream (sync and async), after parsing the LZMA header, short-circuit when the output size is definitively zero — i.e., Bit1 (EOS-marker flag) is not set and UncompressedSize == 0:

// Bit1 (EOS marker flag) means the output size is not stored in the header
// (the LZMA stream itself contains an end-of-stream marker instead), so we
// only short-circuit when the size is explicitly known to be zero.
if (!FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1) && Header.UncompressedSize == 0)
{
    stream.Skip(); // drain remaining bytes — required to advance position in streaming reads
    return Stream.Null;
}

stream.Skip() is necessary because the stream at this point is a ReadOnlySubStream scoped to CompressedSize bytes; draining it keeps the underlying archive stream correctly positioned for sequential (non-seekable) reads.

Changes

  • ZipFilePart.cs / ZipFilePart.Async.cs — early return for known-empty LZMA entries in both sync and async decompression paths
  • Zip.lzma.empty.zip — new test archive containing a single LZMA-compressed zero-byte entry
  • ZipArchiveTests — regression test via seekable ArchiveFactory.OpenArchive
  • ZipReaderTests — regression test via streaming ReaderFactory.OpenReader with a forward-only stream
Original prompt

This section details on the original issue you should resolve

<issue_title>Error when extract zip file contains 0-size entry</issue_title>
<issue_description>SharpCompress version: 0.46.3

Test code:

using SharpCompress.Archives;

try
{
    var zipFile = "test.zip";
    var outDir = "output";
    if (!Directory.Exists(outDir))
        Directory.CreateDirectory(outDir);
    using (var archive = ArchiveFactory.OpenArchive(zipFile))
    {
        archive.WriteToDirectory(outDir);
    }
    Console.WriteLine("Success");
}
catch (Exception ex)
{
    Console.WriteLine(ex.ToString());
}

Test zip file:
test.zip

Test output:

SharpCompress.Compressors.LZMA.DataErrorException: Data Error
   at SharpCompress.Compressors.LZMA.LzmaStream.Read(Byte[] buffer, Int32 offset, Int32 count) in D:\a\sharpcompress\sharpcompress\src\SharpCompress\Compressors\LZMA\LzmaStream.cs:line 305
   at System.IO.Stream.CopyTo(Stream destination, Int32 bufferSize)
   at SharpCompress.Archives.IArchiveEntryExtensions.WriteTo(IArchiveEntry archiveEntry, Stream streamToWriteTo, IProgress`1 progress) in D:\a\sharpcompress\sharpcompress\src\SharpCompress\Archives\IArchiveEntryExtensions.cs:line 28
   at SharpCompress.Archives.IArchiveEntryExtensions.<>c__DisplayClass8_0.<WriteToFile>b__0(String x, FileMode fm) in D:\a\sharpcompress\sharpcompress\src\SharpCompress\Archives\IArchiveEntryExtensions.cs:line 139
   at SharpCompress.Common.ExtractionMethods.WriteEntryToFile(IEntry entry, String destinationFileName, Action`2 openAndWrite) in D:\a\sharpcompress\sharpcompress\src\SharpCompress\Common\ExtractionMethods.cs:line 120
   at SharpCompress.Archives.IArchiveEntryExtensions.WriteToFile(IArchiveEntry entry, String destinationFileName) in D:\a\sharpcompress\sharpcompress\src\SharpCompress\Archives\IArchiveEntryExtensions.cs:line 133
   at SharpCompress.Archives.IArchiveEntryExtensions.<>c__DisplayClass6_0.<WriteToDirectory>b__0(String path) in D:\a\sharpcompress\sharpcompress\src\SharpCompress\Archives\IArchiveEntryExtensions.cs:line 109
   at SharpCompress.Common.ExtractionMethods.WriteEntryToDirectory(IEntry entry, String destinationDirectory, Action`1 write) in D:\a\sharpcompress\sharpcompress\src\SharpCompress\Common\ExtractionMethods.cs:line 85
   at SharpCompress.Archives.IArchiveEntryExtensions.WriteToDirectory(IArchiveEntry entry, String destinationDirectory) in D:\a\sharpcompress\sharpcompress\src\SharpCompress\Archives\IArchiveEntryExtensions.cs:line 106
   at SharpCompress.Archives.IArchiveExtensions.WriteToDirectoryInternal(IArchive archive, String destinationDirectory, IProgress`1 progress) in D:\a\sharpcompress\sharpcompress\src\SharpCompress\Archives\IArchiveExtensions.cs:line 61
   at SharpCompress.Archives.IArchiveExtensions.WriteToDirectory(IArchive archive, String destinationDirectory, IProgress`1 progress) in D:\a\sharpcompress\sharpcompress\src\SharpCompress\Archives\IArchiveExtensions.cs:line 30
   at Program.<Main>$(String[] args) in D:\Code\ConsoleApp2\ConsoleApp2\Program.cs:line 11
```</issue_description>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits February 27, 2026 22:06
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
…entries

Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix error when extracting zip file with 0-size entry Fix DataErrorException when extracting LZMA-compressed zero-byte ZIP entries Feb 27, 2026
@adamhathcock adamhathcock marked this pull request as ready for review February 28, 2026 07:57
Copilot AI review requested due to automatic review settings February 28, 2026 07:57
@kilo-code-bot
Copy link
Contributor

kilo-code-bot bot commented Feb 28, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Overview

The fix addresses a bug where extracting LZMA-compressed zero-byte ZIP entries throws a DataErrorException. The solution correctly short-circuits decompression when the uncompressed size is explicitly known to be zero (i.e., when Bit1 EOS marker flag is not set and UncompressedSize == 0).

Changes Reviewed

File Lines Description
src/SharpCompress/Common/Zip/ZipFilePart.cs 153-165 Added early return for zero-size LZMA entries (sync)
src/SharpCompress/Common/Zip/ZipFilePart.Async.cs 161-173 Added early return for zero-size LZMA entries (async)
tests/SharpCompress.Test/Zip/ZipArchiveTests.cs 900-912 Regression test for Archive API
tests/SharpCompress.Test/Zip/ZipReaderTests.cs 547-568 Regression test for Reader API (streaming)
tests/TestArchives/Archives/Zip.lzma.empty.zip N/A New test archive

Code Quality

  • Logic: Correct - checks both !Bit1 (size stored in header) and UncompressedSize == 0
  • Stream handling: Correct - uses stream.Skip() / stream.SkipAsync() to drain remaining bytes for forward-only streams
  • Return value: Correct - returns Stream.Null for zero-size entries
  • Documentation: Excellent - clear comments explaining the Bit1 flag behavior
  • Tests: Comprehensive - covers both seekable (Archive API) and forward-only (Reader API) scenarios
Files Reviewed (5 files)
  • src/SharpCompress/Common/Zip/ZipFilePart.cs
  • src/SharpCompress/Common/Zip/ZipFilePart.Async.cs
  • tests/SharpCompress.Test/Zip/ZipArchiveTests.cs
  • tests/SharpCompress.Test/Zip/ZipReaderTests.cs
  • tests/TestArchives/Archives/Zip.lzma.empty.zip

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes extraction of ZIP entries that are LZMA-compressed but have a known uncompressed size of 0 bytes, preventing DataErrorException during reads and adding regression coverage for both seekable and streaming readers.

Changes:

  • Short-circuit LZMA decompression for entries with explicitly-known zero uncompressed size (sync + async paths).
  • Add a new LZMA ZIP fixture containing a single zero-byte entry.
  • Add regression tests for both ArchiveFactory.OpenArchive (seekable) and ReaderFactory.OpenReader (streaming/forward-only).

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/SharpCompress/Common/Zip/ZipFilePart.cs Adds early-return logic for known-empty LZMA entries during decompression stream creation.
src/SharpCompress/Common/Zip/ZipFilePart.Async.cs Async equivalent of the LZMA known-empty early-return logic.
tests/TestArchives/Archives/Zip.lzma.empty.zip New test archive fixture containing a zero-byte LZMA-compressed entry.
tests/SharpCompress.Test/Zip/ZipArchiveTests.cs Regression test covering extraction via seekable archive API.
tests/SharpCompress.Test/Zip/ZipReaderTests.cs Regression test covering extraction via streaming reader API with a forward-only stream.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error when extract zip file contains 0-size entry

3 participants