{"record":{"id":"34a15641070ef79c","repo":"can1357/oh-my-pi","slug":"invalid-label-huffman-table-prefix-collision","errorCode":null,"errorMessage":"Invalid ${label} Huffman table: prefix collision","messagePattern":"Invalid (.+?) Huffman table: prefix collision","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":98,"sourceCode":"\t\t\tif (code + counts[length]! > 2 ** length) {\n\t\t\t\tthrow new ArchiveError(`Invalid ${label} Huffman table: oversubscribed codes`);\n\t\t\t}\n\t\t\tnextCodes[length] = code;\n\t\t}\n\t\tif (nextCodes[maximumLength]! + counts[maximumLength]! !== 2 ** maximumLength) {\n\t\t\tthrow new ArchiveError(`Invalid ${label} Huffman table: incomplete codes`);\n\t\t}\n\n\t\tconst tree = new CanonicalHuffman(label);\n\t\tfor (let symbol = 0; symbol < symbolCount; symbol++) {\n\t\t\tconst length = lengths[symbol]!;\n\t\t\tif (length === 0) continue;\n\t\t\tconst symbolCode = nextCodes[length]!;\n\t\t\tnextCodes[length] = symbolCode + 1;\n\t\t\tlet node = 0;\n\t\t\tfor (let bitIndex = length - 1; bitIndex >= 0; bitIndex--) {\n\t\t\t\tif (tree.#symbol[node]! >= 0) {\n\t\t\t\t\tthrow new ArchiveError(`Invalid ${label} Huffman table: prefix collision`);\n\t\t\t\t}\n\t\t\t\tconst bit = (symbolCode >>> bitIndex) & 1;\n\t\t\t\tlet child = bit === 0 ? tree.#zero[node]! : tree.#one[node]!;\n\t\t\t\tif (child < 0) {\n\t\t\t\t\tchild = tree.#symbol.length;\n\t\t\t\t\ttree.#zero.push(-1);\n\t\t\t\t\ttree.#one.push(-1);\n\t\t\t\t\ttree.#symbol.push(-1);\n\t\t\t\t\tif (bit === 0) tree.#zero[node] = child;\n\t\t\t\t\telse tree.#one[node] = child;\n\t\t\t\t}\n\t\t\t\tnode = child;\n\t\t\t}\n\t\t\tif (tree.#symbol[node]! >= 0 || tree.#zero[node]! >= 0 || tree.#one[node]! >= 0) {\n\t\t\t\tthrow new ArchiveError(`Invalid ${label} Huffman table: duplicate code`);\n\t\t\t}\n\t\t\ttree.#symbol[node] = symbol;\n\t\t}","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L80-L116","documentation":"While inserting each symbol's code path into the binary decoding tree, build() finds an interior node already occupied by a symbol (tree.#symbol[node] >= 0) — meaning a previously inserted code is a prefix of the current one. This indicates the length table, though internally consistent by the oversubscription check, does not match canonical assignment invariants (corrupt stream or encoding bug), so decoding would be ambiguous.","triggerScenarios":"Decompressing LZH data whose command/position/temporary table lengths were corrupted after passing the count checks (e.g. bit-rot altering lengths without breaking the Kraft sum), or a custom encoder emitting non-canonical length assignments.","commonSituations":"Damaged archives, hand-edited or fuzzed table sections, mixing compressed blocks from different variants.","solutions":["Validate the archive externally (unlha -t / CRC check) and re-obtain a clean copy","Ensure the table section is being read from the right bit offset (verify method id and header-skip logic before decompressLhStatic)","If writing an encoder, always assign lengths in canonical symbol order from a standard Huffman construction","Catch ArchiveError; do not retry decoding corrupted bytes"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const out = decompressLhStatic(data, size);\n} catch (err) {\n  if (err instanceof ArchiveError) return { ok: false, reason: 'corrupt-huffman-table' };\n  throw err;\n}","preventionTips":["Validate archives with an independent tool before processing","Ensure bit-reader alignment: correct header size and data offset","Use standard Huffman length construction in encoders","Do not attempt recovery decoding on prefix-collision tables"],"tags":["archive","huffman","lzh","corruption","prefix-code"],"backgroundTag":"corrupt-huffman-table","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}