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.

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.

Appendix: 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)