{"record":{"id":"70ac325387e9e164","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-invalid-lzx-huffman-symbol","errorCode":null,"errorMessage":"Invalid CAB archive: invalid LZX Huffman symbol","messagePattern":"Invalid CAB archive: invalid LZX Huffman symbol","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/lzx.ts","lineNumber":114,"sourceCode":"\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) {\n\t\tconst symbol = pretree.decode(reader);\n\t\tif (symbol === 17 || symbol === 18) {\n\t\t\tconst run = reader.readBits(symbol === 17 ? 4 : 5) + (symbol === 17 ? 4 : 20);\n\t\t\tif (index + run > last) throw new ArchiveError(\"Invalid CAB archive: LZX code-length run exceeds its tree\");\n\t\t\tlengths.fill(0, index, index + run);\n\t\t\tindex += run;\n\t\t\tcontinue;\n\t\t}\n\t\tif (symbol === 19) {","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/lzx.ts#L96-L132","documentation":"Thrown by LzxHuffmanTable.decode after consuming up to 16 bits without the accumulated code matching any entry in the tree. The bitstream contains a code prefix that no symbol was assigned, which cannot happen with a valid encoder — it indicates corrupt or misaligned data.","triggerScenarios":"Decoding any LZX symbol (main, length, aligned, or pretree) when the reader is bit-desynchronized or the data is corrupt, so the next bits do not form a valid code prefix.","commonSituations":"Truncated or bit-rotted CAB downloads, wrong slice boundaries passed to decompressFrame (frames must be fed in order with exact CFDATA uncompressed-block sizes), decoding non-CAB LZX data with this CAB-specific decoder.","solutions":["Check that each decompressFrame call receives exactly one CFDATA block's compressed bytes, in order, with matching outputSize.","Verify the CAB file's integrity and re-download.","Recompress the archive with a conformant tool.","Make sure no earlier error path left the decoder mid-frame; a fresh LzxDecoder must be created per folder and reused for the whole folder only."],"exampleFix":"// before\nfor (const block of blocks) decoder.decompressFrame(block.compressed, block.uncompressedSize)\n// after\nlet decoder: LzxDecoder | null = null\nfor (const block of blocks) {\n  decoder ??= new LzxDecoder(folder.windowBits)\n  decoder.decompressFrame(block.compressed, block.uncompressedSize) // same decoder per folder, exact block bytes\n}","handlingStrategy":"try-catch","validationCode":"// Ensure frame boundaries are exact before decoding:\nif (!Number.isInteger(size) || size <= 0 || size > 32768) throw new Error('bad frame size')\nif (bytes.byteLength === 0) throw new Error('empty frame data')","typeGuard":null,"tryCatchPattern":"try {\n  const frame = decoder.decompressFrame(blockData, blockOutSize)\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('invalid LZX Huffman symbol')) {\n    throw new Error('CAB data is corrupt or frames were fed out of order')\n  }\n  throw err\n}","preventionTips":["Feed CFDATA blocks strictly in order with exact byte slices","Use a fresh LzxDecoder for each CAB folder","Never reuse a decoder instance after an error","Verify archive checksums before decompression"],"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"}