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

  1. Re-download/restore the complete archive and confirm byte size matches the source
  2. Test with `xz -t` externally to confirm the file itself is truncated/corrupt
  3. If reading from a stream, buffer the entire input before decoding instead of decoding a partial chunk
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/dde135f124bc593f. Report an issue: GitHub.