can1357/oh-my-pi · error · ArchiveError
Invalid XZ stream: block size does not match its index recor
Error message
Invalid XZ stream: block size does not match its index record
What it means
This ArchiveError is thrown while decoding an XZ block when the block's actual end (check data end) does not line up with the padded block size recorded in the stream index. The index record's unpaddedSize (rounded up to a 4-byte boundary) must exactly match where the block's integrity check ends; a mismatch means the stream is corrupt or the index/block pairing is broken.
Source
Thrown at packages/utils/src/ar/codecs/xz.ts:483
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;
for (const stream of streams)
for (const record of stream.records) {
totalSize += record.uncompressedSize;
if (!Number.isSafeInteger(totalSize) || totalSize > maxOutput)View on GitHub (pinned to 9690622007)
Solutions
- Re-download or re-extract the archive from a trusted source
- Verify the file's checksum against the publisher's published hash
- Test the file with a standalone tool (xz -t) to confirm corruption before reporting a bug
- If concatenating XZ streams yourself, keep each stream's blocks and index intact
Example fix
// before: trusting a partially downloaded file
await xzDecompress(truncatedBytes, limit);
// after: validate integrity first
if ((await Bun.file(path).slice(0).arrayBuffer()) && !(await fileChecksumMatches(path))) throw new Error('archive corrupt');
await xzDecompress(bytes, limit); Defensive patterns
Strategy: validation
Validate before calling
import { isXz } from '@oh-my-pi/pi-utils/ar/codecs/xz';
if (!isXz(bytes)) throw new Error('input is not XZ');
// trust boundary: pre-check integrity out of band (xz -t or published checksum) Try / catch
try {
const out = await xzDecompress(bytes, limit);
} catch (err) {
if (err instanceof ArchiveError && /block size does not match/.test(err.message)) {
// treat as corrupt archive: surface to user / re-fetch
} else throw err;
} Prevention
- Verify archive checksums before decompressing
- Never splice or hand-edit XZ stream bytes
- Use xz -t as an independent pre-flight check on untrusted archives
When it happens
Trigger: Calling xzDecompress (or decoding a .tar.xz/.xz payload) on bytes whose block header/check region and index records disagree — e.g. corrupted archive data, a hand-truncated or spliced XZ file, or an index from a different stream.
Common situations: Partially downloaded or truncated .xz files; byte-level edits to archives; concatenated streams where a block from one stream is paired with an index from another; files damaged in transfer.
Related errors
- Invalid XZ stream: blocks do not align with index
- Invalid XZ stream: ${error instanceof Error ? error.message
- Invalid tar octal value: ${value}
- Truncated embedded addon archive entry: ${filename}
- Invalid ARJ method-4 compressed data: truncated bitstream
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/18f01430c46c8452.
Report an issue: GitHub.