can1357/oh-my-pi · error · ArchiveError

Invalid XZ stream: non-zero block header padding

Error message

Invalid XZ stream: non-zero block header padding

What it means

After parsing the filters, every remaining byte of a block header up to its CRC32 must be zero padding per the XZ spec. A non-zero byte means the header is malformed, so the decoder rejects the stream. This is a strict conformance check on header padding.

Source

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

	if (crc32(bytes.subarray(offset, offset + headerSize - 4)) !== read32LE(bytes, offset + headerSize - 4))
		throw new ArchiveError("Invalid XZ stream: block header CRC32 mismatch");
	const cursor: Cursor = { bytes, pos: offset + 1, limit: offset + headerSize - 4 };
	const flags = bytes[cursor.pos++]!;
	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]!,

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-download/restore the file and validate with `xz -t` before decompressing.
  2. Re-encode the archive with the standard xz tool if it was produced by custom code.
  3. Verify you are not accidentally splicing or rewriting bytes of the archive in your pipeline.
  4. Reject untrusted archives that fail validation instead of attempting repair.
Defensive patterns

Strategy: try-catch

Validate before calling

import { isXz } from '@oh-my-pi/pi-utils/ar/codecs/xz';
if (!isXz(bytes)) throw new Error('Not an XZ stream');

Try / catch

try {
	return await xzDecompress(bytes, maxOutput);
} catch (err) {
	if (err instanceof ArchiveError) throw new Error(`Corrupt XZ archive: ${err.message}`, { cause: err });
	throw err;
}

Prevention

When it happens

Trigger: xzDecompress parses a block header whose filter list ends before the header size boundary and at least one padding byte is non-zero.

Common situations: Hand-crafted or tool-mangled XZ headers, corruption from a bad editor or transfer that overwrote header bytes, or output of a non-conforming custom encoder.

Related errors


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