{"record":{"id":"b3d2ae935772adb6","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-lzx-stream-uses-an-empty-huff","errorCode":null,"errorMessage":"Invalid CAB archive: LZX stream uses an empty Huffman tree","messagePattern":"Invalid CAB archive: LZX stream uses an empty Huffman tree","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/lzx.ts","lineNumber":105,"sourceCode":"\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;\n\t\tfor (let length = 1; length <= 16; length++) {\n\t\t\tcode = code * 2 + reader.readBits(1);\n\t\t\tconst relative = code - this.#firstCodes[length]!;\n\t\t\tif (relative >= 0 && relative < this.#counts[length]!) {\n\t\t\t\treturn this.#symbols[this.#firstSymbols[length]! + relative]!;\n\t\t\t}\n\t\t}\n\t\tthrow new ArchiveError(\"Invalid CAB archive: invalid LZX Huffman symbol\");\n\t}\n}\n\nfunction readCodeLengths(reader: LzxBitReader, lengths: Uint8Array, first: number, last: number): void {\n\tconst pretreeLengths = new Uint8Array(20);\n\tfor (let index = 0; index < pretreeLengths.byteLength; index++) pretreeLengths[index] = reader.readBits(4);\n\tconst pretree = new LzxHuffmanTable(pretreeLengths);\n\tlet index = first;\n\twhile (index < last) {","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/lzx.ts#L87-L123","documentation":"Thrown by LzxHuffmanTable.decode when attempting to decode a symbol from a tree that was constructed with no symbols (empty === true; only possible for tables built with allowEmpty). Decoding requires reading bits and matching them to a code, which is impossible with zero symbols.","triggerScenarios":"Decoding a match length via an empty secondary length tree whose position table maps to a primary length of 7 (matchLength === NUM_PRIMARY_LENGTHS), i.e. the stream says 'read a length symbol' but the length tree has no symbols.","commonSituations":"Corrupted CAB archives where the length tree was encoded as all-zero but the compressed data still references it, or a compressor/decoder mismatch (e.g. a stream produced with different tree conventions).","solutions":["Re-acquire or repair the CAB archive; the stream references a tree that has no codes.","Recompress with a standard CAB tool so referenced trees are populated.","Confirm you are decoding with the same LZX variant (CAB LZX) the data was produced with."],"exampleFix":"// before\nconst extra = this.#lengthTable.decode(reader)\n// after\nif (!this.#lengthTable || this.#lengthTable.empty) throw new ArchiveError(\"Invalid CAB archive: length tree required but empty\")\nconst extra = this.#lengthTable.decode(reader)","handlingStrategy":"try-catch","validationCode":"if (lengthTable && lengthTable.empty) {\n  throw new Error('Stream references an empty length tree — archive is corrupt')\n}","typeGuard":"function isDecodable(table: LzxHuffmanTable | undefined): table is LzxHuffmanTable {\n  return table !== undefined && !table.empty\n}","tryCatchPattern":"try {\n  const out = decoder.decompressFrame(bytes, size)\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('empty Huffman tree')) {\n    throw new Error('CAB archive corrupt: empty tree referenced during decode')\n  }\n  throw err\n}","preventionTips":["Treat any decode attempt against an empty tree as archive corruption, not a recoverable state","Reuse one LzxDecoder per folder so tree state stays consistent","Validate CAB block checksums before decoding"],"tags":["archive","cab","lzx","huffman","decoding"],"backgroundTag":"corrupt-archive-huffman-tree","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}