can1357/oh-my-pi · error · ArchiveError
Invalid CAB archive: oversubscribed LZX Huffman tree
Error message
Invalid CAB archive: oversubscribed LZX Huffman tree
What it means
Thrown by the LzxHuffmanTable constructor when the set of code lengths is not a valid (under-subscribed or exactly complete) Huffman code: at some bit length, the running code space exceeds 2^length, meaning the described tree assigns more codes than exist at that depth. This is a Kraft-inequality violation — the tree cannot be decoded unambiguously.
Source
Thrown at packages/utils/src/ar/codecs/lzx.ts:88
let symbolCount = 0;
for (const length of lengths) {
if (length > 16) throw new ArchiveError("Invalid CAB archive: invalid LZX Huffman code length");
if (length !== 0) {
this.#counts[length]++;
symbolCount++;
}
}
this.empty = symbolCount === 0;
if (this.empty && !allowEmpty) {
throw new ArchiveError("Invalid CAB archive: empty LZX Huffman tree");
}
let code = 0;
let symbolOffset = 0;
for (let length = 1; length <= 16; length++) {
code = (code + this.#counts[length - 1]!) * 2;
if (code + this.#counts[length]! > 2 ** length) {
throw new ArchiveError("Invalid CAB archive: oversubscribed LZX Huffman tree");
}
this.#firstCodes[length] = code;
this.#firstSymbols[length] = symbolOffset;
symbolOffset += this.#counts[length]!;
}
this.#symbols = new Uint16Array(symbolCount);
const next = this.#firstSymbols.slice();
for (let symbol = 0; symbol < lengths.byteLength; symbol++) {
const length = lengths[symbol]!;
if (length !== 0) this.#symbols[next[length]!] = symbol;
next[length]!++;
}
}
decode(reader: LzxBitReader): number {
if (this.empty) throw new ArchiveError("Invalid CAB archive: LZX stream uses an empty Huffman tree");
let code = 0;View on GitHub (pinned to 9690622007)
Solutions
- Validate the CAB file and re-obtain a known-good copy.
- Recompress the archive with a standard CAB tool so trees satisfy the Kraft inequality.
- If building trees programmatically, compute lengths with a real Huffman algorithm (or canonical lengths) instead of hand-picked values.
- Confirm the bit reader offset is correct; desync earlier in the stream produces nonsensical lengths.
Example fix
// before new LzxHuffmanTable(Uint8Array.from([1, 1])) // two length-1 codes: oversubscribed // after new LzxHuffmanTable(Uint8Array.from([1, 2, 2])) // valid canonical code
Defensive patterns
Strategy: try-catch
Validate before calling
function isKraftValid(lengths: Uint8Array): boolean {
let sum = 0
for (const l of lengths) if (l > 0) sum += 2 ** -l
return sum <= 1 + 1e-9
}
// call isKraftValid(lengths) before constructing a table yourself Type guard
function isKraftValid(lengths: Uint8Array): boolean {
let sum = 0
for (const l of lengths) if (l > 0) sum += 2 ** -l
return sum <= 1 + 1e-9
} Try / catch
try {
decoder.decompressFrame(bytes, size)
} catch (err) {
if (err instanceof ArchiveError && err.message.includes('oversubscribed')) {
throw new Error('CAB archive is corrupt: invalid Huffman code definition')
}
throw err
} Prevention
- Generate code lengths with a real Huffman algorithm, never by hand
- Validate archive integrity (checksums) before decoding
- Keep bit-reader state synchronized — never skip bytes mid-frame
When it happens
Trigger: Decompressing a CAB LZX block whose main/length/aligned/pretree code lengths sum (weighted by 2^-length) to more than 1, e.g. two symbols with length 1, or lengths that over-fill a level after earlier levels are counted.
Common situations: Corrupted CAB files, archives written by broken compressors, decoding a non-LZX stream as LZX so garbage bits are interpreted as tree lengths.
Related errors
- Invalid CAB archive: empty LZX Huffman tree
- Invalid CAB archive: invalid LZX Huffman symbol
- Invalid CAB archive: LZX code-length run exceeds its tree
- Invalid CAB archive: invalid LZX Huffman code length
- Invalid CAB archive: LZX stream uses an empty Huffman tree
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/8208a919efdc767e.
Report an issue: GitHub.