{"record":{"id":"8208a919efdc767e","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-oversubscribed-lzx-huffman-tr","errorCode":null,"errorMessage":"Invalid CAB archive: oversubscribed LZX Huffman tree","messagePattern":"Invalid CAB archive: oversubscribed LZX Huffman tree","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/lzx.ts","lineNumber":88,"sourceCode":"\t\tlet symbolCount = 0;\n\t\tfor (const length of lengths) {\n\t\t\tif (length > 16) throw new ArchiveError(\"Invalid CAB archive: invalid LZX Huffman code length\");\n\t\t\tif (length !== 0) {\n\t\t\t\tthis.#counts[length]++;\n\t\t\t\tsymbolCount++;\n\t\t\t}\n\t\t}\n\t\tthis.empty = symbolCount === 0;\n\t\tif (this.empty && !allowEmpty) {\n\t\t\tthrow new ArchiveError(\"Invalid CAB archive: empty LZX Huffman tree\");\n\t\t}\n\n\t\tlet code = 0;\n\t\tlet symbolOffset = 0;\n\t\tfor (let length = 1; length <= 16; length++) {\n\t\t\tcode = (code + this.#counts[length - 1]!) * 2;\n\t\t\tif (code + this.#counts[length]! > 2 ** length) {\n\t\t\t\tthrow new ArchiveError(\"Invalid CAB archive: oversubscribed LZX Huffman tree\");\n\t\t\t}\n\t\t\tthis.#firstCodes[length] = code;\n\t\t\tthis.#firstSymbols[length] = symbolOffset;\n\t\t\tsymbolOffset += this.#counts[length]!;\n\t\t}\n\n\t\tthis.#symbols = new Uint16Array(symbolCount);\n\t\tconst next = this.#firstSymbols.slice();\n\t\tfor (let symbol = 0; symbol < lengths.byteLength; symbol++) {\n\t\t\tconst length = lengths[symbol]!;\n\t\t\tif (length !== 0) this.#symbols[next[length]!] = symbol;\n\t\t\tnext[length]!++;\n\t\t}\n\t}\n\n\tdecode(reader: LzxBitReader): number {\n\t\tif (this.empty) throw new ArchiveError(\"Invalid CAB archive: LZX stream uses an empty Huffman tree\");\n\t\tlet code = 0;","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/lzx.ts#L70-L106","documentation":"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.","triggerScenarios":"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.","commonSituations":"Corrupted CAB files, archives written by broken compressors, decoding a non-LZX stream as LZX so garbage bits are interpreted as tree lengths.","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."],"exampleFix":"// before\nnew LzxHuffmanTable(Uint8Array.from([1, 1])) // two length-1 codes: oversubscribed\n// after\nnew LzxHuffmanTable(Uint8Array.from([1, 2, 2])) // valid canonical code","handlingStrategy":"try-catch","validationCode":"function isKraftValid(lengths: Uint8Array): boolean {\n  let sum = 0\n  for (const l of lengths) if (l > 0) sum += 2 ** -l\n  return sum <= 1 + 1e-9\n}\n// call isKraftValid(lengths) before constructing a table yourself","typeGuard":"function isKraftValid(lengths: Uint8Array): boolean {\n  let sum = 0\n  for (const l of lengths) if (l > 0) sum += 2 ** -l\n  return sum <= 1 + 1e-9\n}","tryCatchPattern":"try {\n  decoder.decompressFrame(bytes, size)\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('oversubscribed')) {\n    throw new Error('CAB archive is corrupt: invalid Huffman code definition')\n  }\n  throw err\n}","preventionTips":["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"],"tags":["archive","cab","lzx","huffman","corruption"],"backgroundTag":"corrupt-archive-huffman-tree","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}