can1357/oh-my-pi · error · ArchiveError
Invalid XZ stream: truncated filter properties
Error message
Invalid XZ stream: truncated filter properties
What it means
A block header declares a filter whose properties blob (propertySize bytes) extends past the end of the header, so the declared property bytes are not present. The decoder throws rather than reading out of bounds. This indicates the block header is inconsistent or the stream is corrupt.
Source
Thrown at packages/utils/src/ar/codecs/xz.ts:447
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);
const compressedSize = record.unpaddedSize - headerSize - integritySize;
if (!Number.isSafeInteger(compressedSize) || compressedSize <= 0)
throw new ArchiveError("Invalid XZ stream: compressed block size is invalid");
if (declaredCompressed !== undefined && declaredCompressed !== compressedSize)
throw new ArchiveError("Invalid XZ stream: block compressed size mismatch");
if (declaredUncompressed !== undefined && declaredUncompressed !== record.uncompressedSize)
throw new ArchiveError("Invalid XZ stream: block uncompressed size mismatch");
const compressedStart = offset + headerSize;
const compressedEnd = compressedStart + compressedSize;
const paddingSize = (4 - ((headerSize + compressedSize) & 3)) & 3;
const checkStart = compressedEnd + paddingSize;
if (checkStart + integritySize > bytes.byteLength) throw new ArchiveError("Invalid XZ stream: truncated block data");View on GitHub (pinned to 9690622007)
Solutions
- Re-acquire the archive and verify its integrity (checksum or `xz -t`).
- Check that the byte buffer passed to xzDecompress is the complete file, not truncated by your own read logic.
- If the file comes from an untrusted source, treat it as invalid input and reject it before decompression.
- Re-compress the data with a standard `xz` encoder if the file came from a custom tool.
Example fix
// before
const bytes = (await Bun.file('a.xz').arrayBuffer()).slice(0, 100); // accidental truncation
await xzDecompress(new Uint8Array(bytes), maxOutput);
// after
const bytes = await Bun.file('a.xz').bytes(); // full file
await xzDecompress(bytes, maxOutput); Defensive patterns
Strategy: validation
Validate before calling
if (bytes.byteLength < 32) throw new Error('File too small to be a valid XZ stream');
if (!isXz(bytes)) throw new Error('Missing XZ magic — refusing to decompress'); Try / catch
try {
const out = await xzDecompress(bytes, maxOutput);
} catch (err) {
if (err instanceof ArchiveError && /truncated/i.test(err.message)) {
throw new Error(`Archive ${name} is corrupt or truncated; re-download it`);
}
throw err;
} Prevention
- Ensure file reads return the complete file (no accidental byte caps).
- Validate downloads against published checksums before extraction.
- Reject archives from untrusted sources that fail format checks.
- Prefer standard xz-produced archives in your build pipeline.
When it happens
Trigger: xzDecompress encounters a block header where a filter's varint propertySize exceeds the bytes remaining between the cursor and the header's CRC field (cursor.limit - cursor.pos).
Common situations: Truncated or bit-corrupted .xz files, archives mangled by an intermediate transfer, or fuzzed/malicious inputs designed to make parsers misbehave.
Related errors
- Invalid XZ stream: truncated block data
- Invalid XZ stream: non-canonical variable-length integer
- Invalid XZ stream: truncated stream framing
- Invalid XZ stream: missing block header
- Invalid XZ stream: truncated block header
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/cefa55bb4ef8876e.
Report an issue: GitHub.