can1357/oh-my-pi · error · ArchiveError

Invalid RAR archive: signature not found

Error message

Invalid RAR archive: signature not found

What it means

indexRarMetadata() scans the head of the stream for the RAR signature ('Rar!' + 0x1a + version bytes, RAR4 or RAR5). If neither marker is found within the probed bytes, the file is not a recognizable RAR archive and this ArchiveError is thrown.

Source

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

async function indexRarMetadata(
	source: ByteSource,
	options: FormatReadOptions,
): Promise<{ marker: { version: 4 | 5; offset: number }; segments: SparseSegment[] }> {
	let probeEnd = Math.min(source.size, RAR5_MARKER.byteLength);
	let probe = await source.read(0, probeEnd);
	let marker = findMarker(probe);
	while (!marker && probeEnd < Math.min(source.size, 1024 * 1024 + RAR5_MARKER.byteLength)) {
		const nextEnd = Math.min(source.size, 1024 * 1024 + RAR5_MARKER.byteLength, Math.max(64 * 1024, probeEnd * 2));
		assertIndexSize(nextEnd, options.limits, "RAR signature scan");
		const next = await source.read(probeEnd, nextEnd);
		const combined = new Uint8Array(nextEnd);
		combined.set(probe);
		combined.set(next, probeEnd);
		probe = combined;
		probeEnd = nextEnd;
		marker = findMarker(probe);
	}
	if (!marker) throw new ArchiveError("Invalid RAR archive: signature not found");
	const segments: SparseSegment[] = [{ start: 0, bytes: probe }];
	let metadataSize = probe.byteLength;
	assertIndexSize(metadataSize, options.limits, "RAR metadata");
	let offset = marker.offset + (marker.version === 5 ? RAR5_MARKER.byteLength : RAR4_MARKER.byteLength);
	while (offset < source.size) {
		if (marker.version === 5) {
			if (offset + 5 > source.size) corrupt("truncated RAR5 header");
			const prefix = await source.read(offset, Math.min(source.size, offset + 16));
			const sizeCursor = { offset: 4 };
			const headerSize = readVint(prefix, sizeCursor, prefix.byteLength, "header size");
			if (headerSize > 2 * 1024 * 1024) corrupt("RAR5 header exceeds format limit");
			const headerEnd = checkedEnd(offset, sizeCursor.offset + headerSize, source.size, "RAR5 header");
			assertIndexSize(metadataSize + headerEnd - offset, options.limits, "RAR metadata");
			const header = await source.read(offset, headerEnd);
			segments.push({ start: offset, bytes: header });
			metadataSize += header.byteLength;
			assertIndexSize(metadataSize, options.limits, "RAR metadata");
			const cursor = { offset: sizeCursor.offset };

View on GitHub (pinned to 9690622007)

Solutions

  1. Confirm the file starts with 'Rar!' (hexdump the first 8 bytes) — if not, obtain the correct file
  2. Re-download in binary mode; avoid ASCII/text transfer modes
  3. For multi-part sets, open the first volume (.part1.rar / .rar), not a later part
Defensive patterns

Strategy: validation

Validate before calling

const head = new Uint8Array(await file.slice(0, 7).arrayBuffer());
if (!(head[0]===0x52 && head[1]===0x61 && head[2]===0x72 && head[3]===0x21 && head[4]===0x1a)) {
  throw new Error('File does not start with the RAR signature');
}

Type guard

function looksLikeRar(head: Uint8Array): boolean {
  return head.length >= 7 && head[0]===0x52 && head[1]===0x61 && head[2]===0x72 && head[3]===0x21 && head[4]===0x1a;
}

Try / catch

try {
  const reader = await readRar(source, options);
} catch (err) {
  if (err instanceof ArchiveError && /signature not found/.test(err.message)) {
    // sniff actual format (zip/7z/tar) and route to the right reader
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling readRar on a file that is not RAR (zip/7z/tar renamed to .rar), an empty file, a text-mode-mangled download, or a RAR file whose first bytes were stripped.

Common situations: Mislabeled downloads from the web; SFTP/FTP ASCII transfers corrupting binary data; truncated files smaller than the marker; opening volume part .r00 instead of .part1.rar.

Related errors


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