can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: zero-length LZX block

Error message

Invalid CAB archive: zero-length LZX block

What it means

When starting a new LZX block, #readBlockHeader reads a 24-bit block length; a length of zero is illegal because every block must produce at least one byte, so the decoder rejects it instead of looping forever on a no-op block. This indicates a corrupt or malformed bitstream.

Source

Thrown at packages/utils/src/ar/codecs/lzx.ts:231

			this.#blockRemaining -= produced;
		}
		if (this.#blockRemaining === 0 && this.#blockType === 3 && this.#uncompressedPadding) {
			reader.readByte();
			this.#uncompressedPadding = false;
		}
		reader.alignWord();

		const translated = this.#translateE8(raw);
		this.#frame++;
		return translated;
	}

	#readBlockHeader(reader: LzxBitReader): void {
		if (this.#blockType === 3 && this.#uncompressedPadding) reader.readByte();
		this.#uncompressedPadding = false;
		this.#blockType = reader.readBits(3);
		this.#blockLength = reader.readBits(16) * 256 + reader.readBits(8);
		if (this.#blockLength === 0) throw new ArchiveError("Invalid CAB archive: zero-length LZX block");
		this.#blockRemaining = this.#blockLength;

		if (this.#blockType === 1 || this.#blockType === 2) {
			if (this.#blockType === 2) {
				const alignedLengths = new Uint8Array(8);
				for (let index = 0; index < alignedLengths.byteLength; index++) alignedLengths[index] = reader.readBits(3);
				this.#alignedTable = new LzxHuffmanTable(alignedLengths);
			}
			readCodeLengths(reader, this.#mainLengths, 0, 256);
			readCodeLengths(reader, this.#mainLengths, 256, this.#mainLengths.byteLength);
			this.#mainTable = new LzxHuffmanTable(this.#mainLengths);
			if (this.#mainLengths[0xe8] !== 0) this.#intelStarted = true;
			readCodeLengths(reader, this.#lengthLengths, 0, this.#lengthLengths.byteLength);
			this.#lengthTable = new LzxHuffmanTable(this.#lengthLengths, true);
			return;
		}
		if (this.#blockType === 3) {
			this.#intelStarted = true;

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify and re-download the CAB archive.
  2. Recompress with a standard CAB tool so every block has a non-zero length.
  3. Check frame/block byte boundaries — desync from a prior error can land the reader on padding interpreted as a zero header.
  4. Never continue using an LzxDecoder instance after a thrown ArchiveError; rebuild it from the folder start.

Example fix

// before
decoder.decompressFrame(bytes.slice(4), size) // arbitrary skip desyncs the bitstream
// after
decoder.decompressFrame(bytes, size) // feed exact CFDATA block bytes; fresh decoder per folder
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const out = decoder.decompressFrame(bytes, size)
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('zero-length LZX block')) {
    throw new Error('CAB archive corrupt: zero-length LZX block header')
  }
  throw err
}

Prevention

When it happens

Trigger: Decoding a CAB LZX stream whose next block header encodes blockLength = 0 (all 24 length bits zero), typically after bit desync or data truncation.

Common situations: Corrupted or truncated downloads, encoders emitting empty blocks, feeding wrong byte offsets so padding bytes are parsed as a block header.

Related errors


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