{"record":{"id":"5b032614ca80fa06","repo":"can1357/oh-my-pi","slug":"invalid-this-label-huffman-code-excessive-dep","errorCode":null,"errorMessage":"Invalid ${this.#label} Huffman code: excessive depth","messagePattern":"Invalid (.+?) Huffman code: excessive depth","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":128,"sourceCode":"\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;\n\tconst encodedCount = reader.read(5);\n\tif (encodedCount === 0) return CanonicalHuffman.single(reader.read(5), symbolCount, label);","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L110-L146","documentation":"decode() also enforces a hard depth cap of 16 bits (LZH's maximum code length). If no leaf is reached after consuming 16 bits, the code is longer than any the table permits, so the stream cannot correspond to this table. This is a safety net that also prevents infinite loops on pathological/corrupt data.","triggerScenarios":"Corrupt data bits forming a never-terminating path, or a table built with max length < 16 while the data contains longer patterns — always a stream/table mismatch, seen via decompressLhStatic or temporary-tree decoding on damaged input.","commonSituations":"Severely corrupted or scrambled archives, wrong-offset parsing where data bytes are actually still table bytes, fuzzed inputs.","solutions":["Treat the archive as corrupt: verify externally and re-obtain a clean copy","Recheck where the table section ends and the data section begins (9-bit command count / 5-bit temp count boundaries)","Do not retry on the same bytes; ArchiveError here is deterministic","If you generate archives, cap code lengths at 16 bits (package-merge) so stream and table always agree"],"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 && err.message.includes('excessive depth')) {\n    throw new Error('archive stream/table mismatch (corrupt data)');\n  }\n  throw err;\n}","preventionTips":["Validate archives externally before processing","Cap encoder code lengths at 16 bits so stream and table always agree","Confirm table/data boundary offsets before decoding","Quarantine files that hit the depth cap — they are corrupt or adversarial"],"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"}