can1357/oh-my-pi · error · ArchiveError

Invalid ARJ method-4 compressed data: match exceeds declared

Error message

Invalid ARJ method-4 compressed data: match exceeds declared size

What it means

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.

Source

Thrown at packages/utils/src/ar/arj.ts:139

function decompressArjMethod4(packed: Uint8Array, outSize: number): Uint8Array {
	const reader = new ArjBitReader(packed);
	const output = new Uint8Array(outSize);
	let outputPosition = 0;
	while (outputPosition < outSize) {
		let lengthCode = 0;
		let lengthWidth = 0;
		for (; lengthWidth < 7; lengthWidth++) {
			if (reader.read(1) === 0) break;
			lengthCode += 2 ** lengthWidth;
		}
		if (lengthWidth !== 0) lengthCode += reader.read(lengthWidth);
		if (lengthCode === 0) {
			output[outputPosition++] = reader.read(8);
			continue;
		}
		const length = lengthCode + 2;
		if (length > outSize - outputPosition) {
			throw new ArchiveError("Invalid ARJ method-4 compressed data: match exceeds declared size");
		}
		let positionCode = 0;
		let positionWidth = 9;
		for (; positionWidth < 13; positionWidth++) {
			if (reader.read(1) === 0) break;
			positionCode += 2 ** positionWidth;
		}
		positionCode += reader.read(positionWidth);
		if (positionCode >= 26_624 || positionCode >= outputPosition) {
			throw new ArchiveError("Invalid ARJ method-4 compressed data: history distance is out of range");
		}
		let sourcePosition = outputPosition - positionCode - 1;
		for (let index = 0; index < length; index++) output[outputPosition++] = output[sourcePosition++]!;
	}
	reader.assertZeroPadding();
	return output;
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Check that outSize equals the member's original (uncompressed) size from the ARJ header — this is the most common cause when calling decompressArjMethod4 directly.
  2. Confirm the packed bytes belong to that member (correct offset and compressedSize from the index).
  3. Test the archive externally to confirm the member is intact; if corrupt, restore from a known-good copy.
  4. If you truncated output intentionally (partial extraction), decompress with the full declared size and slice afterwards — never shrink outSize.

Example fix

// before: passing compressed size as the output size
const out = decompressArjMethod4(packed, entry.compressedSize);
// after: use the declared original size
const out = decompressArjMethod4(packed, entry.originalSize);
Defensive patterns

Strategy: validation

Validate before calling

// outSize must be the member's original (uncompressed) size, not its compressed size
if (typeof entry.originalSize !== "number" || entry.originalSize < 0) {
  throw new Error(`Member ${entry.name}: missing original size for method-4 decompression`);
}
const out = decompressArjMethod4(packed, entry.originalSize);

Try / catch

try {
  return decompressArjMethod4(packed, originalSize);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("match exceeds declared size")) {
    throw new Error(`Member ${name}: declared size ${originalSize} disagrees with compressed stream`);
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/8043d159e1ea206e. Report an issue: GitHub.