{"record":{"id":"a91420f6983c282a","repo":"can1357/oh-my-pi","slug":"invalid-arj-method-4-compressed-data-history-dist","errorCode":null,"errorMessage":"Invalid ARJ method-4 compressed data: history distance is out of range","messagePattern":"Invalid ARJ method-4 compressed data: history distance is out of range","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/arj.ts","lineNumber":149,"sourceCode":"\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\nclass ArjMemberSource implements MemberSource {\n\treadonly #archive: Uint8Array;\n\treadonly #start: number;\n\treadonly #packedSize: number;\n\treadonly #method: number;\n\treadonly #crc: number;\n\n\tconstructor(archive: Uint8Array, start: number, packedSize: number, method: number, crc: number) {\n\t\tthis.#archive = archive;\n\t\tthis.#start = start;","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/arj.ts#L131-L167","documentation":"In method 4, a match's distance (positionCode) must reference bytes already emitted: positionCode < outputPosition, and the window is bounded at 26,624. The decoder throws when the distance is out of this range — referencing data that does not exist yet — because copying from it would read uninitialized output.","triggerScenarios":"decompressArjMethod4(): decoded positionCode is >= 26,624 or >= outputPosition. Happens with a match declared near the start of the stream (outputPosition too small), corrupted position-code bits, or a stream whose first bytes are not literal bytes as the format requires.","commonSituations":"Corrupted packed data flipping position bits, a spliced/truncated stream that starts mid-file so early matches point before the buffer, fuzzed archives probing the window bound, or an encoder bug emitting back-references before enough history exists.","solutions":["Confirm the packed payload starts at the member's first byte — starting mid-stream leaves early matches with no history and triggers this error.","Test the member externally (`arj t` / 7-Zip); corruption here usually means the whole archive is damaged — restore from backup.","Ensure the decompressor is fed the exact compressedSize bytes for this member only, not a region spanning adjacent members.","If you are writing a method-4 encoder, never emit a match with distance >= bytes already produced; start streams with literal bytes."],"exampleFix":"// before: feeding a mid-file slice into the decompressor\nconst packed = bytes.subarray(fileOffset + 100, fileOffset + 100 + entry.compressedSize);\n// after: feed the member's full packed stream from its start\nconst packed = bytes.subarray(entry.dataOffset, entry.dataOffset + entry.compressedSize);\nconst out = decompressArjMethod4(packed, entry.originalSize);","handlingStrategy":"try-catch","validationCode":"// Feed the decompressor the member's complete packed stream from its very first byte\nif (entry.dataOffset < 0 || entry.dataOffset + entry.compressedSize > bytes.byteLength) {\n  throw new Error(`Member ${entry.name}: packed data out of bounds`);\n}\nconst packed = bytes.subarray(entry.dataOffset, entry.dataOffset + entry.compressedSize);","typeGuard":null,"tryCatchPattern":"try {\n  return decompressArjMethod4(packed, originalSize);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"history distance is out of range\")) {\n    throw new Error(`Member ${name}: corrupt back-reference — packed data likely damaged or mis-sliced`);\n  }\n  throw err;\n}","preventionTips":["Never start decompression mid-stream; LZ back-references need full history from byte 0.","Slice per-member payload by header offsets so streams do not span members.","Test archives externally before batch extraction; this error usually indicates corruption."],"tags":["archive","decompression","lz77","corrupt-file"],"backgroundTag":"lz-backreference-out-of-range","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}