can1357/oh-my-pi · error · ArchiveError
Invalid XZ stream: truncated block header
Error message
Invalid XZ stream: truncated block header
What it means
The first byte of an XZ block header encodes the header size as ((byte / 4) - 1), so the computed size must be a multiple of 4 within spec bounds; the library also requires the header to fit within the remaining buffer and be at least 8 bytes (2 size units). It throws when the computed header size runs past the end of the data or is below the minimum, i.e. the block header is truncated or nonsensical.
Source
Thrown at packages/utils/src/ar/codecs/xz.ts:433
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++) {
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)View on GitHub (pinned to 9690622007)
Solutions
- Re-download/restore the complete archive and confirm byte size matches the source
- Test with `xz -t` externally to confirm the file itself is truncated/corrupt
- If reading from a stream, buffer the entire input before decoding instead of decoding a partial chunk
- Check that no upstream code altered offsets into the buffer (e.g. skipping the stream header incorrectly)
Example fix
// before: passing a partial stream of unknown completeness
const head = buf.subarray(0, 1024);
await decodeXz(head);
// after: guard for completeness or read the full file
const full = new Uint8Array(await Bun.file('archive.xz').arrayBuffer());
await decodeXz(full); Defensive patterns
Strategy: validation
Validate before calling
const bytes = new Uint8Array(await Bun.file('archive.xz').arrayBuffer());
if (bytes.byteLength < 60) throw new Error('File too small to contain a complete XZ stream'); Type guard
null
Try / catch
try {
return await decodeXz(bytes);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes('truncated block header')) {
throw new Error('XZ block header is cut off — input truncated; fetch the complete archive');
}
throw err;
} Prevention
- Complete the download and verify byte count before decoding
- Don't slice streams for parallel decode unless each slice is a full independent block/stream
- Keep offsets consistent with the stream header parsing this decoder performs
- Run `xz -t` on files from unknown producers
When it happens
Trigger: Decoding a truncated XZ file where the block header region is cut off; a corrupted first header byte yielding headerSize < 8 or a size that overruns the buffer.
Common situations: Partial downloads; files sliced or streamed incorrectly; bit corruption in the size byte; fuzzed inputs.
Related errors
- Invalid XZ stream: missing block header
- Invalid XZ stream: block header CRC32 mismatch
- Invalid XZ stream: padding without a stream
- Invalid XZ stream: truncated stream framing
- Invalid XZ stream: footer magic mismatch
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/dde135f124bc593f.
Report an issue: GitHub.