can1357/oh-my-pi · error · ArchiveError

ARJ member '${memberPath}' extracted to an unexpected size

Error message

ARJ member '${memberPath}' extracted to an unexpected size

What it means

This ArchiveError is thrown as a post-decompression invariant: the decompressor's output length does not equal the size declared in the member's local header. For stored members this is caught earlier, so this usually indicates a decoder produced fewer/more bytes than requested for methods 1–4, or the header's size field is wrong.

Source

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

			case 2:
			case 3:
				output = decompressLhStatic(packed, size, 26_624, 5, 17, `ARJ method ${this.#method}`);
				break;
			case 4:
				output = decompressArjMethod4(packed, size);
				break;
			case 8:
			case 9:
				if (size !== 0 || packed.byteLength !== 0) {
					throw new ArchiveError(`ARJ member '${memberPath}' has invalid no-data method sizes`);
				}
				output = new Uint8Array(0);
				break;
			default:
				throw new ArchiveError(`ARJ member '${memberPath}' uses unsupported compression method ${this.#method}`);
		}
		if (output.byteLength !== size)
			throw new ArchiveError(`ARJ member '${memberPath}' extracted to an unexpected size`);
		if (this.#method !== 8 && crc32(output) !== this.#crc) {
			throw new ArchiveError(`ARJ member '${memberPath}' failed CRC32 verification`);
		}
		return output;
	}
}

function normalizeHostPath(rawPath: string, hostOs: number): string {
	let path = rawPath.replaceAll("\\", "/");
	if (hostOs === 1) path = path.replaceAll(">", "/");
	else if (hostOs === 4) path = path.replaceAll(":", "/");
	return path;
}

/** Probe whether bytes begin with a CRC-framed ARJ main header. */
export function sniffArj(bytes: Uint8Array): boolean {
	if (bytes.byteLength < 34 || bytes[0] !== ARJ_SIGNATURE_0 || bytes[1] !== ARJ_SIGNATURE_1) return false;
	const size = u16(bytes, 2);

View on GitHub (pinned to 9690622007)

Solutions

  1. Test the archive with the official arj tool (arj t) to confirm corruption.
  2. Re-download or restore the archive from a good copy.
  3. Re-create the archive with a compliant tool.
  4. If the header size is genuinely wrong, patch the extraction to trust the decompressed length only when it matches, and skip members that fail.

Example fix

// before
const data = await member.read(member.size, member.path);
// after
try {
  const data = await member.read(member.size, member.path);
} catch (e) {
  if (e instanceof ArchiveError && e.message.includes('unexpected size')) {
    console.warn(`member ${member.path} size mismatch; archive header may be corrupt`);
    return null;
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const data = await member.read(size, path);
} catch (e) {
  if (e instanceof ArchiveError && e.message.includes('unexpected size')) {
    // archive header or compressed stream is corrupt; skip member
  } else throw e;
}

Prevention

When it happens

Trigger: Calling member.read(size, memberPath) where decompressLhStatic (methods 1–3) or decompressArjMethod4 (method 4) returns an output whose byteLength differs from size — typically because the packed stream is corrupt or the declared uncompressed size is wrong.

Common situations: Bit-rot or partial downloads corrupting compressed streams, archives written by buggy tools with wrong size fields, mixed-method archives assembled incorrectly.

Related errors


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