{"record":{"id":"f1cb822388635af1","repo":"can1357/oh-my-pi","slug":"invalid-label-command-huffman-table-size","errorCode":null,"errorMessage":"Invalid ${label} command Huffman table size","messagePattern":"Invalid (.+?) command Huffman table size","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":165,"sourceCode":"\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`);\n\tconst lengths = new Uint8Array(symbolCount);\n\tlet index = 0;\n\twhile (index < encodedCount) {\n\t\tconst code = temporary.decode(reader);\n\t\tif (code <= 2) {\n\t\t\tconst skipped = code === 0 ? 1 : code === 1 ? reader.read(4) + 3 : reader.read(9) + 20;\n\t\t\tif (index + skipped > encodedCount) throw new ArchiveError(`Invalid ${label} command Huffman table`);\n\t\t\tindex += skipped;\n\t\t} else {\n\t\t\tlengths[index++] = code - 2;\n\t\t}\n\t}\n\treturn CanonicalHuffman.build(lengths, symbolCount, label);\n}\n\nfunction readPositionTree(\n\treader: MsbBitReader,\n\tpositionBits: number,","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L147-L183","documentation":"readCommandTree() reads a 9-bit encodedCount for the command table (510 symbols for LH5/LH6, larger spaces handled by the caller). A count above symbolCount cannot occur in a valid stream, so the library throws ArchiveError before allocating/reading the length array. This protects against corrupt headers and prevents out-of-range table parsing.","triggerScenarios":"decompressLhStatic on a stream whose 9-bit command count field is corrupted or misread — e.g. parsing an LH7 block with an LH5-sized reader, byte-offset errors from a bad LHA header, or fuzzed inputs with counts > 510.","commonSituations":"Truncated or damaged .lzh files, variant mismatches (LH4/LH5/LH6/LH7 confusion), misparsed extended headers shifting the data start, fuzzing.","solutions":["Check the archive's method byte and use the matching table size / decompressor path","Recompute the compressed-data start offset (header parse, extended-header skip) before decompressLhStatic","Validate the file externally and restore a clean copy if checks fail","Catch ArchiveError and report a corrupt or unsupported archive; retrying is pointless"],"exampleFix":"// before: single fixed-size path for all variants\nconst commandTree = readCommandTree(reader, tempTree, 'command');\n// after: route by declared method\nif (method === 'LH7') { /* 510+ symbol path */ } else { const commandTree = readCommandTree(reader, tempTree, 'command'); }","handlingStrategy":"try-catch","validationCode":"// Verify offsets and method before decompressing:\nif (!isSupportedLhMethod(method)) throw new Error(`unsupported method: ${method}`);\nif (dataStart + declaredCompressedSize > data.length) throw new Error('truncated archive');","typeGuard":"function isSupportedLhMethod(method: string): boolean {\n  return ['LH4', 'LH5', 'LH6', 'LH7'].includes(method);\n}","tryCatchPattern":"try {\n  const out = decompressLhStatic(data.subarray(dataStart), originalSize);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('table size')) {\n    throw new Error('command table header corrupt or LH-variant mismatch');\n  }\n  throw err;\n}","preventionTips":["Route LH7 (larger symbol spaces) to the correct decompressor path","Parse extended LHA headers fully so the data offset is exact","Validate archive integrity externally before batch extraction","Do not retry decoding after a table-size rejection — the input is corrupt"],"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"}