can1357/oh-my-pi · error · ArchiveError

Invalid XZ stream: non-zero block padding

Error message

Invalid XZ stream: non-zero block padding

What it means

Between the end of the compressed data and the integrity check, XZ blocks are padded with zero bytes to a 4-byte boundary. A non-zero byte in that gap means the block layout does not match the index record's geometry, so the stream is rejected as invalid.

Source

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

	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)
		throw new ArchiveError("Invalid XZ stream: decoded block size mismatch");
	output = output.slice();
	for (let index = filters.length - 2; index >= 0; index--) applyFilter(output, filters[index]!);
	for (let index = compressedEnd; index < checkStart; index++)
		if (bytes[index] !== 0) throw new ArchiveError("Invalid XZ stream: non-zero block padding");
	verifyCheck(checkId, output, bytes.subarray(checkStart, checkStart + integritySize));
	const paddedEnd = offset + Math.ceil(record.unpaddedSize / 4) * 4;
	if (checkStart + integritySize !== paddedEnd)
		throw new ArchiveError("Invalid XZ stream: block size does not match its index record");
	return output;
}

/** Whether bytes begin with the XZ stream-header magic. */
export function isXz(bytes: Uint8Array): boolean {
	return bytes.byteLength >= XZ_MAGIC.byteLength && equalBytes(bytes.subarray(0, XZ_MAGIC.byteLength), XZ_MAGIC);
}

/** Decompress all concatenated streams in an XZ container within `maxOutput`. */
export async function xzDecompress(bytes: Uint8Array, maxOutput: number): Promise<Uint8Array> {
	if (!Number.isSafeInteger(maxOutput) || maxOutput < 0) throw new ArchiveError("Invalid XZ output limit");
	try {
		const streams = discoverStreams(bytes);
		let totalSize = 0;

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-acquire an intact copy of the archive and verify with `xz -t`.
  2. Re-encode with the standard xz tool if a custom producer wrote the stream.
  3. Avoid any post-processing that rewrites archive bytes in place.
  4. Treat repeated occurrences as evidence of a faulty producer or failing storage medium.
Defensive patterns

Strategy: try-catch

Validate before calling

const ok = await $`xz -t ${path}`.quiet().nothrow();
if (ok.exitCode !== 0) throw new Error(`XZ integrity check failed: ${path}`);

Try / catch

try {
	return await xzDecompress(bytes, maxOutput);
} catch (err) {
	if (err instanceof ArchiveError && /non-zero block padding/i.test(err.message)) {
		throw new Error('Archive block layout invalid — file was likely modified or corrupted');
	}
	throw err;
}

Prevention

When it happens

Trigger: xzDecompress scans bytes from compressedEnd to checkStart after decompression and finds any non-zero padding byte.

Common situations: Archives damaged after creation (bytes overwritten in the padding region), hand-spliced streams, or non-conforming encoders that write junk instead of zero padding.

Related errors


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