{"record":{"id":"13b866c5755c0103","repo":"can1357/oh-my-pi","slug":"invalid-label-position-huffman-table-size","errorCode":null,"errorMessage":"Invalid ${label} position Huffman table size","messagePattern":"Invalid (.+?) position Huffman table size","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":189,"sourceCode":"\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,\n\tsymbolCount: number,\n\tlabel: string,\n): CanonicalHuffman {\n\tconst encodedCount = reader.read(positionBits);\n\tif (encodedCount === 0) return CanonicalHuffman.single(reader.read(positionBits), symbolCount, label);\n\tif (encodedCount > symbolCount) throw new ArchiveError(`Invalid ${label} position Huffman table size`);\n\tconst lengths = new Uint8Array(symbolCount);\n\tfor (let index = 0; index < encodedCount; index++) lengths[index] = readCodeLength(reader, label);\n\treturn CanonicalHuffman.build(lengths, symbolCount, label);\n}\n\n/** @internal Decode the static-Huffman LZSS stream shared by LZH and ARJ methods 1-3. */\nexport function decompressLhStatic(\n\tpacked: Uint8Array,\n\toutSize: number,\n\tdictionarySize: number,\n\tpositionBits: number,\n\tpositionSymbols: number,\n\tlabel: string,\n): Uint8Array {\n\tconst reader = new MsbBitReader(packed, label);\n\tconst output = new Uint8Array(outSize);\n\tlet outputPosition = 0;\n\tlet blockRemaining = 0;","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L171-L207","documentation":"readPositionTree reads the count of encoded position symbols; if that count exceeds the position symbol space (positionSymbols), the table cannot be valid. This guards against corrupt bits or wrong positionBits/symbolCount parameters.","triggerScenarios":"decompressLhStatic on data whose position-table symbol count field (read with positionBits bits) yields a value greater than positionSymbols — corrupt stream, or positionBits/positionSymbols mismatched with the actual method (e.g. -lh5- vs -lh7- tables).","commonSituations":"Decoding -lh7-/ARJ method 3 data with -lh5- parameters (positionSymbols too small); bitmisalignment after a bad command table; fuzzed inputs.","solutions":["Pass the correct positionBits/positionSymbols for the method being decoded (e.g. larger table for -lh6-/-lh7-)","Re-extract or validate the archive source; check CRC","Verify the bit reader starts at the true start of the compressed stream","Handle ArchiveError for untrusted inputs"],"exampleFix":"// before\ndecompressLhStatic(packed, outSize, 5, 256, \"LZH -lh5-\"); // data is actually -lh7-\n// after\n// -lh7- uses 20-bit positions / larger symbol table\ndecompressLhStatic(packed, outSize, 20, 1024, \"LZH -lh7-\");","handlingStrategy":"validation","validationCode":"// positionSymbols must be large enough for the method's positionBits\nif (2 ** positionBits > positionSymbols) throw new Error(\"position table parameters inconsistent for this method\");","typeGuard":null,"tryCatchPattern":"try {\n  const out = decompressLhStatic(packed, outSize, positionBits, positionSymbols, label);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"position Huffman\")) return null;\n  throw err;\n}","preventionTips":["Use the correct parameter set per method (-lh4-/-lh5-/-lh6-/-lh7-/ARJ 1-3)","Validate archive checksums before decompressing","Never reuse hardcoded parameters across archive variants"],"tags":["archive","corruption","huffman","lzh"],"backgroundTag":"corrupt-archive-data","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}