{"record":{"id":"37445f6d29875a92","repo":"can1357/oh-my-pi","slug":"invalid-label-temporary-huffman-table-size","errorCode":null,"errorMessage":"Invalid ${label} temporary Huffman table size","messagePattern":"Invalid (.+?) temporary Huffman table size","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":147,"sourceCode":"\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);\n\tif (encodedCount > symbolCount) throw new ArchiveError(`Invalid ${label} temporary Huffman table size`);\n\tconst lengths = new Uint8Array(symbolCount);\n\tlet index = 0;\n\twhile (index < encodedCount) {\n\t\tlengths[index++] = readCodeLength(reader, label);\n\t\tif (index === 3) {\n\t\t\tconst skipped = reader.read(2);\n\t\t\tif (index + skipped > encodedCount) throw new ArchiveError(`Invalid ${label} temporary Huffman table`);\n\t\t\tindex += skipped;\n\t\t}\n\t}\n\treturn CanonicalHuffman.build(lengths, symbolCount, label);\n}\n\nfunction readCommandTree(reader: MsbBitReader, temporary: CanonicalHuffman, label: string): CanonicalHuffman {\n\tconst symbolCount = 510;\n\tconst encodedCount = reader.read(9);\n\tif (encodedCount === 0) return CanonicalHuffman.single(reader.read(9), symbolCount, label);\n\tif (encodedCount > symbolCount) throw new ArchiveError(`Invalid ${label} command Huffman table size`);","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L129-L165","documentation":"readTemporaryTree() reads a 5-bit encodedCount declaring how many code lengths follow for the 19-symbol temporary table. Values above 19 (symbolCount) are impossible in a valid LH stream, so the library rejects them rather than reading out of bounds. encodedCount === 0 is legal and selects the single-symbol fallback.","triggerScenarios":"DecompressLhStatic encountering a corrupted or misaligned table header where the 5-bit count is garbage (e.g. data section parsed as table, or LH-variant mismatch), or fuzzed input with counts like 20-31.","commonSituations":"Wrong start offset from a misparsed LHA header, corrupt downloads, mixing LH4-style streams with an LH5+ parser, fuzzing.","solutions":["Verify the archive's method id (LH5/LH6/LH7) matches the decompressor being used","Recheck header-size computation so the bit reader starts exactly at the table section","Re-obtain a clean copy if external validation fails","Catch ArchiveError and report corruption; do not retry"],"exampleFix":"// before: assuming LH5 table layout for every archive\nconst tree = readTemporaryTree(reader, 'temporary');\n// after: dispatch on the archive's declared method\nif (method === 'LH4') { /* different table routine */ } else { const tree = readTemporaryTree(reader, 'temporary'); }","handlingStrategy":"try-catch","validationCode":"// Cheap pre-checks before invoking the decompressor:\nif (data.length < 2) throw new Error('compressed block too small');\n// method byte sanity (extracted from the LHA header you parsed):\nif (!/^L[HZ][4567]$/.test(method)) throw new Error(`unsupported method ${method}`);","typeGuard":"function isSupportedLhMethod(method: string): boolean {\n  return ['LH4', 'LH5', 'LH6', 'LH7'].includes(method);\n}","tryCatchPattern":"try {\n  const out = decompressLhStatic(data, size);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('table size')) {\n    throw new Error('archive table header is corrupt or method variant mismatched');\n  }\n  throw err;\n}","preventionTips":["Match the decompressor variant to the archive's method id","Recompute data-start offset from the full header (including extended headers)","Validate archives with an external tool before batch processing","Reject suspicious files early instead of feeding them to the decoder"],"tags":["archive","huffman","lzh","corruption","bounds"],"backgroundTag":"corrupt-huffman-table","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}