can1357/oh-my-pi · error · ArchiveError

Invalid XZ stream: block header CRC32 mismatch

Error message

Invalid XZ stream: block header CRC32 mismatch

What it means

Every XZ block header ends with a CRC32 of the header bytes that precede it. The library computes CRC32 over headerSize-4 bytes and compares with the trailing little-endian 4 bytes. A mismatch means the block header bytes were altered after encoding — the header is corrupt even if structurally parseable.

Source

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

	if (checkId === 4) {
		const actual = crc64(output);
		let stored = 0n;
		for (let index = 0; index < 8; index++) stored |= BigInt(expected[index]!) << BigInt(index * 8);
		if (actual !== stored) throw new ArchiveError("Invalid XZ stream: block CRC64 mismatch");
		return;
	}
	const actual = new Uint8Array(new Bun.CryptoHasher("sha256").update(output).digest());
	if (!equalBytes(actual, expected)) throw new ArchiveError("Invalid XZ stream: block SHA-256 mismatch");
}

async function decodeBlock(bytes: Uint8Array, offset: number, record: XzRecord, checkId: number): Promise<Uint8Array> {
	if (offset >= bytes.byteLength || bytes[offset] === 0)
		throw new ArchiveError("Invalid XZ stream: missing block header");
	const headerSize = (bytes[offset]! + 1) * 4;
	if (offset + headerSize > bytes.byteLength || headerSize < 8)
		throw new ArchiveError("Invalid XZ stream: truncated block header");
	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);

View on GitHub (pinned to 9690622007)

Solutions

  1. Replace the file with a known-good copy; header CRC failure means the bytes are damaged, not misconfigured
  2. Verify with `xz -t file.xz` to confirm corruption independently
  3. Re-compress from original data if no good copy exists
  4. If input is untrusted, reject with a clear 'corrupt archive' message rather than attempting repair

Example fix

// before: hand-patching a block header byte
buf[offset + 2] = 0x21; // tweak filter flags
await decodeXz(buf);
// after: recompute or, better, re-compress the archive properly
// $ xz -k -f file.bin && use the fresh file.xz
Defensive patterns

Strategy: try-catch

Validate before calling

const t = await $`xz -t archive.xz`.quiet().nothrow();
if (t.exitCode !== 0) throw new Error('archive.xz header integrity check failed');

Type guard

null

Try / catch

try {
  return await decodeXz(bytes);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('block header CRC32 mismatch')) {
    throw new Error('XZ block header corrupted (header CRC32); archive is damaged');
  }
  throw err;
}

Prevention

When it happens

Trigger: Decoding an XZ stream where any byte in a block header (size byte, flags, filter definitions) was modified, so the header's trailing CRC32 no longer matches.

Common situations: Byte-level corruption from bad storage/transfer; deliberate tampering; buggy tools that patch archive headers in place.

Related errors


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