{"record":{"id":"67922675373ebf99","repo":"can1357/oh-my-pi","slug":"invalid-label-compressed-data-match-exceeds-de","errorCode":null,"errorMessage":"Invalid ${label} compressed data: match exceeds declared size","messagePattern":"Invalid (.+?) compressed data: match exceeds declared size","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":226,"sourceCode":"\tlet commands: CanonicalHuffman | undefined;\n\tlet positions: CanonicalHuffman | undefined;\n\twhile (outputPosition < outSize) {\n\t\tif (blockRemaining === 0) {\n\t\t\tblockRemaining = reader.read(16);\n\t\t\tif (blockRemaining === 0) throw new ArchiveError(`Invalid ${label} compressed data: empty block`);\n\t\t\tconst temporary = readTemporaryTree(reader, label);\n\t\t\tcommands = readCommandTree(reader, temporary, label);\n\t\t\tpositions = readPositionTree(reader, positionBits, positionSymbols, label);\n\t\t}\n\t\tblockRemaining--;\n\t\tconst symbol = commands!.decode(reader);\n\t\tif (symbol < 256) {\n\t\t\toutput[outputPosition++] = symbol;\n\t\t\tcontinue;\n\t\t}\n\t\tconst length = symbol - 256 + 3;\n\t\tif (length > outSize - outputPosition) {\n\t\t\tthrow new ArchiveError(`Invalid ${label} compressed data: match exceeds declared size`);\n\t\t}\n\t\tconst positionCode = positions!.decode(reader);\n\t\tlet distance = positionCode;\n\t\tif (positionCode > 1) {\n\t\t\tconst lowBitCount = positionCode - 1;\n\t\t\tdistance = 2 ** lowBitCount + reader.read(lowBitCount);\n\t\t}\n\t\tif (distance >= dictionarySize || distance >= outputPosition) {\n\t\t\tthrow new ArchiveError(`Invalid ${label} compressed data: history distance is out of range`);\n\t\t}\n\t\tlet sourcePosition = outputPosition - distance - 1;\n\t\tfor (let index = 0; index < length; index++) output[outputPosition++] = output[sourcePosition++]!;\n\t}\n\tif (blockRemaining !== 0) throw new ArchiveError(`Invalid ${label} compressed data: block exceeds declared size`);\n\treader.assertZeroPadding();\n\treturn output;\n}\n","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L208-L244","documentation":"A decoded match (back-reference) would write past outSize, the declared uncompressed size. The library refuses to overflow the caller-provided output buffer, which indicates corrupt commands or a wrong outSize.","triggerScenarios":"decompressLhStatic decodes a command symbol whose implied length (symbol-256+3) exceeds remaining output space — corrupt stream, or outSize computed smaller than the real uncompressed size (e.g. wrong u64 header parse or wrong header field).","commonSituations":"Header vs actual-data mismatch after editing an archive; wrong original-size field from a damaged extended header; fuzzed inputs.","solutions":["Re-read the original-size field from the correct header location (including extended headers)","Validate the archive source / re-download","Catch ArchiveError and report the entry as corrupt instead of writing partial output"],"exampleFix":"// before\nconst outSize = u64(header, 7); // wrong offset, too small\n// after\nconst outSize = sizeFromHeader(header); // use parsed real size field","handlingStrategy":"try-catch","validationCode":"if (outSize <= 0 || !Number.isSafeInteger(outSize)) throw new Error(\"invalid declared output size\");","typeGuard":null,"tryCatchPattern":"try {\n  const out = decompressLhStatic(packed, outSize, positionBits, positionSymbols, label);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"match exceeds\")) return null;\n  throw err;\n}","preventionTips":["Read the original-size field from the correct header offset (including extended-header size overrides)","Verify CRC before extraction","Never hand-edit archive headers"],"tags":["archive","corruption","lzh","buffer-overflow"],"backgroundTag":"corrupt-archive-data","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}