can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: missing LZX decode trees

Error message

Invalid CAB archive: missing LZX decode trees

What it means

#decodeRun tries to Huffman-decode symbols but the main and length decode trees were never built (a Verbatim/Aligned block header was not parsed for this block). This indicates internal state misuse or a corrupt stream where block setup was skipped.

Source

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

			this.#r0 = reader.readUInt32LE();
			this.#r1 = reader.readUInt32LE();
			this.#r2 = reader.readUInt32LE();
			if (this.#r0 === 0 || this.#r1 === 0 || this.#r2 === 0) {
				throw new ArchiveError("Invalid CAB archive: invalid LZX repeated offset");
			}
			this.#uncompressedPadding = (this.#blockLength & 1) !== 0;
			return;
		}
		throw new ArchiveError(`Invalid CAB archive: unsupported LZX block type ${this.#blockType}`);
	}

	#decodeRun(reader: LzxBitReader, output: Uint8Array, outputStart: number, count: number): number {
		if (this.#blockType === 3) {
			for (let index = 0; index < count; index++) this.#writeByte(reader.readByte(), output, outputStart + index);
			return count;
		}
		if (!this.#mainTable || !this.#lengthTable)
			throw new ArchiveError("Invalid CAB archive: missing LZX decode trees");

		let produced = 0;
		while (produced < count) {
			const element = this.#mainTable.decode(reader);
			if (element < 256) {
				this.#writeByte(element, output, outputStart + produced);
				produced++;
				continue;
			}

			const match = element - 256;
			let matchLength = match & NUM_PRIMARY_LENGTHS;
			if (matchLength === NUM_PRIMARY_LENGTHS) matchLength += this.#lengthTable.decode(reader);
			matchLength += MIN_MATCH;
			if (matchLength > count - produced || matchLength > this.#blockRemaining - produced) {
				throw new ArchiveError("Invalid CAB archive: LZX match crosses a frame or block boundary");
			}

View on GitHub (pinned to 9690622007)

Solutions

  1. Ensure decompressFrame is used end-to-end rather than invoking internal decode pieces directly.
  2. Validate the archive; if the header parsed but trees are missing, the stream is corrupt.
  3. Re-obtain the archive from a trusted source.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const out = decompressFrame(frame);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("missing LZX decode trees")) {
    // stream state desync — mark archive corrupt
  } else throw err;
}

Prevention

When it happens

Trigger: decompressFrame -> #decodeRun called with blockType 1 or 2 while #mainTable or #lengthTable is null (trees not initialized via #readBlockHeader).

Common situations: A CAB payload whose block header parsing diverged earlier (bad block type/length) leaving trees unset; calling low-level decode APIs out of order.

Related errors


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