{"record":{"id":"8043d159e1ea206e","repo":"can1357/oh-my-pi","slug":"invalid-arj-method-4-compressed-data-match-exceed","errorCode":null,"errorMessage":"Invalid ARJ method-4 compressed data: match exceeds declared size","messagePattern":"Invalid ARJ method-4 compressed data: match exceeds declared size","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/arj.ts","lineNumber":139,"sourceCode":"function decompressArjMethod4(packed: Uint8Array, outSize: number): Uint8Array {\n\tconst reader = new ArjBitReader(packed);\n\tconst output = new Uint8Array(outSize);\n\tlet outputPosition = 0;\n\twhile (outputPosition < outSize) {\n\t\tlet lengthCode = 0;\n\t\tlet lengthWidth = 0;\n\t\tfor (; lengthWidth < 7; lengthWidth++) {\n\t\t\tif (reader.read(1) === 0) break;\n\t\t\tlengthCode += 2 ** lengthWidth;\n\t\t}\n\t\tif (lengthWidth !== 0) lengthCode += reader.read(lengthWidth);\n\t\tif (lengthCode === 0) {\n\t\t\toutput[outputPosition++] = reader.read(8);\n\t\t\tcontinue;\n\t\t}\n\t\tconst length = lengthCode + 2;\n\t\tif (length > outSize - outputPosition) {\n\t\t\tthrow new ArchiveError(\"Invalid ARJ method-4 compressed data: match exceeds declared size\");\n\t\t}\n\t\tlet positionCode = 0;\n\t\tlet positionWidth = 9;\n\t\tfor (; positionWidth < 13; positionWidth++) {\n\t\t\tif (reader.read(1) === 0) break;\n\t\t\tpositionCode += 2 ** positionWidth;\n\t\t}\n\t\tpositionCode += reader.read(positionWidth);\n\t\tif (positionCode >= 26_624 || positionCode >= outputPosition) {\n\t\t\tthrow new ArchiveError(\"Invalid ARJ method-4 compressed data: history distance is out of range\");\n\t\t}\n\t\tlet sourcePosition = outputPosition - positionCode - 1;\n\t\tfor (let index = 0; index < length; index++) output[outputPosition++] = output[sourcePosition++]!;\n\t}\n\treader.assertZeroPadding();\n\treturn output;\n}\n","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/arj.ts#L121-L157","documentation":"Method 4 is an LZ77-style scheme: a length code decodes to a match of `lengthCode + 2` bytes copied from earlier output. The decoder validates that the match fits within the declared output size (outSize); a match that would overrun it means the stream's declared size and its contents disagree, so it throws instead of writing past the output buffer.","triggerScenarios":"decompressArjMethod4(): the decoded length (lengthCode+2) exceeds outSize - outputPosition at the current output offset. Caused by a corrupted length code, an outSize that is smaller than the original uncompressed size, or a stream concatenated from the wrong member.","commonSituations":"Passing an incorrect outSize (e.g. using the compressed size or a stale index entry) when calling the decompressor directly, corrupt length bits from data damage, or a fuzzed archive crafted to overrun buffers.","solutions":["Check that outSize equals the member's original (uncompressed) size from the ARJ header — this is the most common cause when calling decompressArjMethod4 directly.","Confirm the packed bytes belong to that member (correct offset and compressedSize from the index).","Test the archive externally to confirm the member is intact; if corrupt, restore from a known-good copy.","If you truncated output intentionally (partial extraction), decompress with the full declared size and slice afterwards — never shrink outSize."],"exampleFix":"// before: passing compressed size as the output size\nconst out = decompressArjMethod4(packed, entry.compressedSize);\n// after: use the declared original size\nconst out = decompressArjMethod4(packed, entry.originalSize);","handlingStrategy":"validation","validationCode":"// outSize must be the member's original (uncompressed) size, not its compressed size\nif (typeof entry.originalSize !== \"number\" || entry.originalSize < 0) {\n  throw new Error(`Member ${entry.name}: missing original size for method-4 decompression`);\n}\nconst out = decompressArjMethod4(packed, entry.originalSize);","typeGuard":null,"tryCatchPattern":"try {\n  return decompressArjMethod4(packed, originalSize);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"match exceeds declared size\")) {\n    throw new Error(`Member ${name}: declared size ${originalSize} disagrees with compressed stream`);\n  }\n  throw err;\n}","preventionTips":["Always pass the header-declared original size as outSize; never reuse compressedSize or stale index values.","For partial extraction, decompress fully then slice — do not shrink outSize.","Validate index entries against headers before decompression."],"tags":["archive","decompression","buffer-overflow","corrupt-file"],"backgroundTag":"lz-decode-size-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}