can1357/oh-my-pi · error · ArchiveError

Invalid XZ stream: compressed block size is invalid

Error message

Invalid XZ stream: compressed block size is invalid

What it means

The block's compressed size is derived from the stream index record: unpaddedSize minus header size minus integrity-check size. If that arithmetic is not a safe positive integer, the index record contradicts the header and the stream is invalid. The decoder throws instead of reading a bogus span.

Source

Thrown at packages/utils/src/ar/codecs/xz.ts:456

	if ((flags & 0x3c) !== 0) throw new ArchiveError("Unsupported XZ block flags");
	const filterCount = (flags & 3) + 1;
	const declaredCompressed = (flags & 0x40) !== 0 ? readVarInt(cursor) : undefined;
	const declaredUncompressed = (flags & 0x80) !== 0 ? readVarInt(cursor) : undefined;
	const filters: XzFilter[] = [];
	for (let index = 0; index < filterCount; index++) {
		const id = readVarInt(cursor);
		const propertySize = readVarInt(cursor);
		if (propertySize > cursor.limit - cursor.pos)
			throw new ArchiveError("Invalid XZ stream: truncated filter properties");
		filters.push({ id, properties: bytes.slice(cursor.pos, cursor.pos + propertySize) });
		cursor.pos += propertySize;
	}
	while (cursor.pos < cursor.limit)
		if (bytes[cursor.pos++] !== 0) throw new ArchiveError("Invalid XZ stream: non-zero block header padding");
	const integritySize = checkSize(checkId);
	const compressedSize = record.unpaddedSize - headerSize - integritySize;
	if (!Number.isSafeInteger(compressedSize) || compressedSize <= 0)
		throw new ArchiveError("Invalid XZ stream: compressed block size is invalid");
	if (declaredCompressed !== undefined && declaredCompressed !== compressedSize)
		throw new ArchiveError("Invalid XZ stream: block compressed size mismatch");
	if (declaredUncompressed !== undefined && declaredUncompressed !== record.uncompressedSize)
		throw new ArchiveError("Invalid XZ stream: block uncompressed size mismatch");
	const compressedStart = offset + headerSize;
	const compressedEnd = compressedStart + compressedSize;
	const paddingSize = (4 - ((headerSize + compressedSize) & 3)) & 3;
	const checkStart = compressedEnd + paddingSize;
	if (checkStart + integritySize > bytes.byteLength) throw new ArchiveError("Invalid XZ stream: truncated block data");
	const last = filters[filters.length - 1]!;
	if (last.id !== 0x21 || last.properties.byteLength !== 1)
		throw new ArchiveError(`Unsupported XZ terminal filter ID 0x${last.id.toString(16)} (LZMA2 required)`);
	let output = await lzma2Decompress(
		last.properties[0]!,
		bytes.subarray(compressedStart, compressedEnd),
		record.uncompressedSize,
	);
	if (output.byteLength !== record.uncompressedSize)

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the archive with `xz -t`; a corrupted index usually means the whole file is unusable — re-download it.
  2. Do not splice or concatenate XZ streams/blocks manually; keep whole streams intact.
  3. If you control the producer, ensure the stream index accurately records unpadded/uncompressed sizes per block.
  4. Wrap decompression in error handling so corrupt archives surface as clean failures rather than crashes.
Defensive patterns

Strategy: try-catch

Validate before calling

if (!isXz(bytes)) throw new Error('Not an XZ stream');
if (bytes.byteLength === 0) throw new Error('Empty archive buffer');

Try / catch

try {
	return await xzDecompress(bytes, maxOutput);
} catch (err) {
	if (err instanceof ArchiveError) {
		logger.error('XZ index/header inconsistent', { message: err.message });
		return null;
	}
	throw err;
}

Prevention

When it happens

Trigger: xzDecompress processes a block whose index record's unpaddedSize is smaller than headerSize + integritySize (or yields a non-integer), so compressedSize computes to <= 0 or non-safe-integer.

Common situations: Corrupted stream indexes (bit rot), archives assembled by splicing blocks from different streams, or truncated files whose index was reconstructed incorrectly.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/9d40b5960fb717f9. Report an issue: GitHub.