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
- Replace the file with a known-good copy; header CRC failure means the bytes are damaged, not misconfigured
- Verify with `xz -t file.xz` to confirm corruption independently
- Re-compress from original data if no good copy exists
- 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
- Never patch bytes inside compressed archives; re-compress instead
- Verify transfer integrity end-to-end (checksums from source)
- Keep archives off known-failing storage; watch for repeated corruption patterns
- Fail closed on header CRC errors — the payload cannot be trusted either
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
- Invalid XZ stream: block CRC32 mismatch
- Invalid XZ stream: truncated block header
- Invalid XZ stream: padding without a stream
- Invalid XZ stream: footer magic mismatch
- Invalid XZ stream: footer CRC32 mismatch
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d3b68c306d9091e0.
Report an issue: GitHub.