{"record":{"id":"fc1feb9d9deecc9b","repo":"can1357/oh-my-pi","slug":"invalid-lzh-lzs-data-match-exceeds-declared-siz","errorCode":null,"errorMessage":"Invalid LZH -lzs- data: match exceeds declared size","messagePattern":"Invalid LZH -lzs- data: match exceeds declared size","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":265,"sourceCode":"\tconst output = new Uint8Array(outSize);\n\tconst history = new Uint8Array(2048);\n\thistory.fill(0x20);\n\tlet historyPosition = 2048 - 17;\n\tlet outputPosition = 0;\n\tconst emit = (value: number): void => {\n\t\tif (outputPosition >= outSize) throw new ArchiveError(\"Invalid LZH -lzs- data: output exceeds declared size\");\n\t\toutput[outputPosition++] = value;\n\t\thistory[historyPosition] = value;\n\t\thistoryPosition = (historyPosition + 1) & 2047;\n\t};\n\twhile (outputPosition < outSize) {\n\t\tif (reader.read(1) !== 0) {\n\t\t\temit(reader.read(8));\n\t\t} else {\n\t\t\tconst position = reader.read(11);\n\t\t\tconst length = reader.read(4) + 2;\n\t\t\tif (length > outSize - outputPosition)\n\t\t\t\tthrow new ArchiveError(\"Invalid LZH -lzs- data: match exceeds declared size\");\n\t\t\tfor (let index = 0; index < length; index++) emit(history[(position + index) & 2047]!);\n\t\t}\n\t}\n\treader.assertZeroPadding();\n\treturn output;\n}\n\nconst ZERO_CRC16_BYTES = new Uint8Array(2);\n\nfunction crc16WithZeroRange(bytes: Uint8Array, zeroStart: number, zeroEnd: number): number {\n\tif (zeroStart < 0) return crc16Arc(bytes);\n\tlet value = crc16Arc(bytes.subarray(0, zeroStart));\n\tvalue = crc16Arc(ZERO_CRC16_BYTES.subarray(0, zeroEnd - zeroStart), value);\n\treturn crc16Arc(bytes.subarray(zeroEnd), value);\n}\n\nfunction u16(bytes: Uint8Array, offset: number): number {\n\treturn bytes[offset]! | (bytes[offset + 1]! << 8);","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L247-L283","documentation":"In -lzs- mode, a match is an 11-bit history position plus 4-bit length; if the resulting length exceeds remaining output space, the library refuses to write past outSize.","triggerScenarios":"decompressLzs decodes a match whose length (4-bit+2) exceeds outSize - outputPosition — corrupt bitstream, or outSize smaller than the real uncompressed size.","commonSituations":"Wrong size field parsed from header; archives truncated/damaged in transit; fuzzed inputs.","solutions":["Verify the member's declared size matches the data (check CRC)","Re-obtain the archive","Catch ArchiveError for untrusted input and skip the entry"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (packed.length < 2) throw new Error(\"-lzs- stream too short\");\nif (!Number.isSafeInteger(outSize) || outSize <= 0) throw new Error(\"bad outSize\");","typeGuard":null,"tryCatchPattern":"try {\n  const out = decompressLzs(packed, outSize);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"match exceeds\")) return null;\n  throw err;\n}","preventionTips":["Validate CRC of the source archive","Confirm the declared size matches the member's real content","Handle untrusted archives with ArchiveError-aware control flow"],"tags":["archive","corruption","lzs"],"backgroundTag":"corrupt-archive-data","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}