can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: LZX code-length run exceeds its tree

Error message

Invalid CAB archive: LZX code-length run exceeds its tree

What it means

Thrown by readCodeLengths when a pretree symbol 17 or 18 (zero-run of length 4-19 or 20-51) would extend past the last tree index being defined. The run would write outside the tree's declared symbol range, so the block header is malformed.

Source

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

			const relative = code - this.#firstCodes[length]!;
			if (relative >= 0 && relative < this.#counts[length]!) {
				return this.#symbols[this.#firstSymbols[length]! + relative]!;
			}
		}
		throw new ArchiveError("Invalid CAB archive: invalid LZX Huffman symbol");
	}
}

function readCodeLengths(reader: LzxBitReader, lengths: Uint8Array, first: number, last: number): void {
	const pretreeLengths = new Uint8Array(20);
	for (let index = 0; index < pretreeLengths.byteLength; index++) pretreeLengths[index] = reader.readBits(4);
	const pretree = new LzxHuffmanTable(pretreeLengths);
	let index = first;
	while (index < last) {
		const symbol = pretree.decode(reader);
		if (symbol === 17 || symbol === 18) {
			const run = reader.readBits(symbol === 17 ? 4 : 5) + (symbol === 17 ? 4 : 20);
			if (index + run > last) throw new ArchiveError("Invalid CAB archive: LZX code-length run exceeds its tree");
			lengths.fill(0, index, index + run);
			index += run;
			continue;
		}
		if (symbol === 19) {
			const run = reader.readBits(1) + 4;
			if (index + run > last) throw new ArchiveError("Invalid CAB archive: LZX code-length run exceeds its tree");
			const delta = pretree.decode(reader);
			const length = (lengths[index]! - delta + 17) % 17;
			lengths.fill(length, index, index + run);
			index += run;
			continue;
		}
		lengths[index] = (lengths[index]! - symbol + 17) % 17;
		index++;
	}
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify CAB integrity and re-obtain the archive.
  2. Recompress with a standard tool so code-length runs fit the tree bounds.
  3. Check the block header bit position; earlier desync turns ordinary symbols into spurious run codes.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  decoder.decompressFrame(bytes, size)
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('code-length run exceeds')) {
    throw new Error('CAB archive corrupt: malformed block header')
  }
  throw err
}

Prevention

When it happens

Trigger: Decoding a Verbatim (type 1) or Aligned (type 2) LZX block where an encoded zero-run near the end of the main/length/aligned tree description claims more trailing zeros than remain between first and last.

Common situations: Corrupted CAB archives, compressors emitting run lengths that overrun the tree size, bit desync causing a normal symbol to be misread as a run symbol.

Related errors


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