can1357/oh-my-pi · error · ArchiveError

Invalid XZ stream: block CRC64 mismatch

Error message

Invalid XZ stream: block CRC64 mismatch

What it means

XZ check type 4 is CRC64. After decoding a block the library computes a CRC64 over the uncompressed data and compares it to the 8 stored little-endian bytes. A mismatch means the block's contents do not match the encoder's integrity hash — the input stream is corrupt or altered.

Source

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

		case 11:
			riscvDecode(bytes, startOffset);
			break;
		default:
			throw new ArchiveError(`Unsupported XZ filter ID 0x${filter.id.toString(16)}`);
	}
}

function verifyCheck(checkId: number, output: Uint8Array, expected: Uint8Array): void {
	if (checkId === 0) return;
	if (checkId === 1) {
		if (read32LE(expected, 0) !== crc32(output)) throw new ArchiveError("Invalid XZ stream: block CRC32 mismatch");
		return;
	}
	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;

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-download or restore the archive; the mismatch indicates real data damage
  2. Confirm externally via `xz -t file.xz` that the file fails integrity there too
  3. Retry the transfer if the source is a flaky network or medium, then re-verify
  4. Surface a 'corrupt archive' error to users instead of attempting decompression of known-bad bytes

Example fix

// before: assuming a transient bug and retrying decode
await decodeXz(buf); await decodeXz(buf);
// after: replace the source file on integrity failure
catch (e) { if (String(e.message).includes('CRC64 mismatch')) await reFetchArchive(); }
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 failed integrity check');

Type guard

null

Try / catch

try {
  return await decodeXz(bytes);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('CRC64 mismatch')) {
    throw new Error('XZ block data is corrupt (CRC64); re-download the archive');
  }
  throw err;
}

Prevention

When it happens

Trigger: Decoding an XZ stream created with `xz --check=crc64` (a common default) where any block's stored CRC64 differs from the computed CRC64 of its decoded output.

Common situations: Incomplete or corrupted downloads; failed copies to removable media; archives modified in place; memory/disk faults during compression producing a bad stored hash.

Related errors


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