can1357/oh-my-pi · error · ArchiveError

Invalid RAR archive: ${reason}

Error message

Invalid RAR archive: ${reason}

What it means

Generic corruption guard for RAR archives: `corrupt(reason)` throws ArchiveError("Invalid RAR archive: <reason>") whenever structural parsing assumptions fail — bad signatures, truncated headers, out-of-bounds offsets, missing mandatory headers (e.g. 'RAR4 main header is missing'). The interpolated `reason` names the specific structural problem found (packages/utils/src/ar/rar.ts:734).

Source

Thrown at packages/utils/src/ar/rar.ts:734

function filetimeMs(bytes: Uint8Array, offset: number): number {
	const ticks = readUInt32LE(bytes, offset) + readUInt32LE(bytes, offset + 4) * 0x100000000;
	return ticks / 10000 - 11644473600000;
}

function checkedEnd(start: number, size: number, limit: number, what: string): number {
	if (!Number.isSafeInteger(start) || !Number.isSafeInteger(size) || size < 0) corrupt(`invalid ${what} range`);
	const end = start + size;
	if (!Number.isSafeInteger(end) || end < start || end > limit) corrupt(`truncated ${what}`);
	return end;
}

function need(start: number, size: number, end: number, what: string): void {
	checkedEnd(start, size, end, what);
}

function corrupt(reason: string): never {
	throw new ArchiveError(`Invalid RAR archive: ${reason}`);
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-download or re-copy the archive and verify its checksum/hash matches the source.
  2. Validate the archive externally (`unrar t archive.rar` or `7z t archive.rar`) to confirm it opens elsewhere; if it doesn't, the file is corrupt, not a library issue.
  3. Confirm the file is actually a RAR (magic bytes: Rar!\x1a\x07 for RAR4/RAR5) and wasn't renamed from another format.
  4. Check transfer mode — re-transfer over binary mode (FTP binary, no text transformations).
  5. Catch ArchiveError, inspect the `reason` suffix, and surface it to the user so they can repair/re-obtain the archive.

Example fix

// before: trusting an unverified download
const reader = await openArchive(archivePath);

// after: verify integrity before parsing
const ok = await $`7z t ${archivePath}`.quiet().nothrow();
if (ok.exitCode !== 0) throw new Error(`${archivePath} failed integrity check; re-download it`);
const reader = await openArchive(archivePath);
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify magic bytes + transfer integrity before parsing
const sig = new Uint8Array(await Bun.file(p).slice(0, 7).arrayBuffer());
const validSig = sig[0] === 0x52 && sig[1] === 0x61 && sig[2] === 0x72 && sig[3] === 0x21 && sig[4] === 0x1a && sig[5] === 0x07;
if (!validSig) throw new Error(`${p} is not a RAR archive (bad signature)`);
// optionally: const t = await $`unrar t ${p}`.quiet().nothrow(); if (t.exitCode !== 0) throw ...

Type guard

function isRarMagic(bytes: Uint8Array): boolean {
  return bytes.length >= 6 &&
    bytes[0] === 0x52 && bytes[1] === 0x61 && bytes[2] === 0x72 &&
    bytes[3] === 0x21 && bytes[4] === 0x1a && bytes[5] === 0x07;
}

Try / catch

try {
  const reader = await openArchive(p);
  return reader;
} catch (err) {
  if (err instanceof ArchiveError && err.message.startsWith('Invalid RAR archive:')) {
    throw new Error(`${p} is corrupt (${err.message}); re-download and verify its checksum`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Any call that parses RAR data (listing, reading entries) when the byte stream violates RAR structure: truncated download, flipped/invalid signature bytes, offsets running past buffer end via `need()`/`checkedEnd()`, missing RAR4 main header, or malformed header fields.

Common situations: Incomplete HTTP download or interrupted transfer; archive corrupted in storage/transit; someone renamed a non-RAR file to .rar; text-mode (FTP/ASCII) transfer mangling binary data; edited or partially deleted archive file.

Related errors


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