{"record":{"id":"398627d002325660","repo":"can1357/oh-my-pi","slug":"invalid-this-label-huffman-code","errorCode":null,"errorMessage":"Invalid ${this.#label} Huffman code","messagePattern":"Invalid (.+?) Huffman code","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":126,"sourceCode":"\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}\n\t\treturn tree;\n\t}\n\n\tdecode(reader: MsbBitReader): number {\n\t\tlet node = 0;\n\t\tfor (let depth = 0; depth <= 16; depth++) {\n\t\t\tconst symbol = this.#symbol[node]!;\n\t\t\tif (symbol >= 0) return symbol;\n\t\t\tnode = reader.read(1) === 0 ? this.#zero[node]! : this.#one[node]!;\n\t\t\tif (node < 0) throw new ArchiveError(`Invalid ${this.#label} Huffman code`);\n\t\t}\n\t\tthrow new ArchiveError(`Invalid ${this.#label} Huffman code: excessive depth`);\n\t}\n}\n\nfunction readCodeLength(reader: MsbBitReader, label: string): number {\n\tlet length = reader.read(3);\n\tif (length === 7) {\n\t\twhile (reader.read(1) !== 0) {\n\t\t\tlength++;\n\t\t\tif (length > 16) throw new ArchiveError(`Invalid ${label} Huffman table: code is too long`);\n\t\t}\n\t}\n\treturn length;\n}\n\nfunction readTemporaryTree(reader: MsbBitReader, label: string): CanonicalHuffman {\n\tconst symbolCount = 19;","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L108-L144","documentation":"CanonicalHuffman.decode() walks the binary tree one bit at a time; if the next-bit child link is negative (node < 0), the bit sequence read so far is not a prefix of any code in the table. The library throws ArchiveError because the encoded data stream no longer matches the declared table — the archive data is corrupt or misaligned.","triggerScenarios":"Calling decompressLhStatic / temporary decoding on data where the compressed bitstream diverges from the table: corruption inside the data section, reading the data section at a wrong offset (bad header skip), or continuing to decode past the true end of the block.","commonSituations":"Truncated downloads cut mid-block, archives with wrong declared compressed sizes, byte-shifted parsing after a header bug, fuzz inputs.","solutions":["Check the archive integrity (CRC / unlha -t) and re-download or restore from backup","Verify the compressed-data offset equals headerSize + 2 (method+checksum) for the LH variant in use","Stop decoding at the declared compressed size instead of running to end-of-input","Catch ArchiveError and report a corrupt archive; retrying will not help"],"exampleFix":"// before: decoding until reader is exhausted\ndecodeUntilEnd(bitReader, tree);\n// after: respect the declared compressed size\nconst end = offset + compressedSize;\nwhile (bitReader.byteOffset < end) output.push(tree.decode(bitReader));","handlingStrategy":"try-catch","validationCode":"// Bounds-check against the declared compressed size before decoding:\nif (data.length < declaredCompressedSize) {\n  throw new Error('truncated archive: data shorter than declared size');\n}","typeGuard":null,"tryCatchPattern":"try {\n  const out = decompressLhStatic(data, originalSize);\n} catch (err) {\n  if (err instanceof ArchiveError) {\n    throw new Error(`archive data does not match its Huffman table: ${err.message}`);\n  }\n  throw err;\n}","preventionTips":["Verify file integrity (size + CRC) before decompression","Stop decoding at the declared compressed size, not end-of-buffer","Recheck header parsing so the data section starts at the right byte","Treat decode failures as fatal corruption; never retry the same bytes"],"tags":["archive","huffman","lzh","decode","corruption"],"backgroundTag":"huffman-decode-failure","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}