can1357/oh-my-pi · error · ArchiveError
Invalid XZ stream: block SHA-256 mismatch
Error message
Invalid XZ stream: block SHA-256 mismatch
What it means
XZ check type 10 is SHA-256. The library hashes each decoded block with Bun's SHA-256 CryptoHasher and compares to the 32 bytes stored in the block. A mismatch means the decompressed output differs from what the encoder committed — the stream is corrupt, truncated, or was tampered with.
Source
Thrown at packages/utils/src/ar/codecs/xz.ts:425
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;
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++) {View on GitHub (pinned to 9690622007)
Solutions
- Replace the file with a verified copy from the original source
- Cross-check with the xz CLI (`xz -t`) to confirm the corruption is in the file, not the decoder
- Re-compress the original data if the source archive is unrecoverable
- Treat the input as untrusted and reject it with a clear integrity error message
Example fix
// before: ignoring integrity errors
catch { /* proceed with partial data */ }
// after: fail closed on hash mismatch
catch (e) { if (String(e.message).includes('SHA-256 mismatch')) throw new Error('Archive integrity check failed'); } 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('SHA-256 mismatch')) {
throw new Error('XZ block data is corrupt (SHA-256); archive must be replaced');
}
throw err;
} Prevention
- Use xz's default CRC64 check unless SHA-256 is required and you verify end-to-end
- Confirm any post-compression transformation of the file (encryption wrappers, signatures) doesn't touch the XZ bytes
- Keep original data so corrupt archives can be re-compressed
- Treat integrity mismatch as fail-closed; never emit partial decompressed data
When it happens
Trigger: Decoding an XZ stream created with `xz --check=sha256` where a block's stored 32-byte SHA-256 does not equal the hash of the block's decoded output.
Common situations: Archives stored on failing disks; corrupted cloud sync; deliberate modification of archive contents; producer-side bugs writing wrong check bytes.
Related errors
- Invalid XZ stream: block CRC32 mismatch
- Invalid XZ stream: block CRC64 mismatch
- ASAR member '${formatArchivePathForError(memberPath)}' faile
- Invalid XZ stream: padding without a stream
- Invalid XZ stream: footer magic mismatch
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/537423352f9cad9b.
Report an issue: GitHub.