Guide

Limits and partial reads

An output limit so a bomb stays small and partial reads for streams that break halfway

A limit on the way out

A megabyte of zeros gzips to about a thousand bytes. That ratio is the whole point of compression, and also the whole point of a zip bomb. So decompress stops at 256 MiB by default, and you can set it lower:

ts
decompress("deflate", bomb, { container: "gzip", limit: 100_000 });
// LimitError: deflate: output passes the limit of 100000 bytes

The check happens before a byte lands, not after the buffer has grown. A size the stream claims up front only sizes the first buffer, and never past the limit or 64 MiB. A liar gets a small buffer and the same error.

identify and peel take the same limit, applied to every attempt.

The tools are stricter

BoundValue
Output of one stream64 MiB
Decompressed bytes shown in one answer16 KiB by default, up to 1 MiB with length
Input to one call4,000,000 characters of base64, hex or text
Layers peel takes off10

Long output comes in windows. The header says showing 0 to 16384; pass offset for the rest, and the model asks for the next window with offset.

Partial reads

A stream cut in half still holds half the data. Ask for it:

ts
decompress("deflate", cut, { container: "gzip", partial: true });
// { bytes: …everything before the cut…,
//   details: { error: "deflate: gzip member ends before its CRC-32 and size" } }

Without partial, the same bytes ride on the error:

ts
try {
  decompress("deflate", cut, { container: "gzip" });
} catch (error) {
  if (error instanceof DecompressError) error.partial;  // the same bytes
}

Every format writes as it goes, so what comes out before the damage is real output, not a guess. A wrong checksum works the same way: the bytes are all there, the check just failed. LZMA2 decodes a cut chunk as far as it gets. Inflate never writes a block end it didn't actually read.

Errors

ErrorWhen
DecompressErrorThe stream is broken. Carries format, offset and partial
ChecksumErrorA checksum or a stored size doesn't match. A DecompressError too
LimitErrorThe output passes limit
UnsupportedErrorThe stream uses something the format allows and this package doesn't do, such as a zstd dictionary
UnknownFormatErrorNo format by that name. gzip, xz and friends get pointed to their family
InvalidOptionErrorAn option out of range or for another container

All of them extend CompressionError, so one catch takes them all.