can1357/oh-my-pi · error · ArchiveError

Multi-volume ARJ archives are unsupported

Error message

Multi-volume ARJ archives are unsupported

What it means

Bit 2 (0x04) of the ARJ main header flags byte indicates a multi-volume (split across disks/files) archive. The reader parses a single in-memory byte buffer only and cannot follow continuation volumes, so it rejects such archives immediately after reading the main header.

Source

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

	let bytes: Uint8Array;
	try {
		bytes = await readAllBytes(source);
	} catch (error) {
		if (error instanceof ArchiveError) throw error;
		throw new ArchiveError(`Unable to read ARJ archive: ${error instanceof Error ? error.message : String(error)}`);
	}
	if (bytes.byteLength !== source.size) throw new ArchiveError("Invalid ARJ archive: truncated data");
	if (!sniffArj(bytes)) throw new ArchiveError("Invalid ARJ archive header");

	const main = parseArjBlock(bytes, 0, options);
	if (main.isEnd) throw new ArchiveError("Invalid ARJ archive: missing main header");
	const mainFirstHeaderSize = bytes[main.bodyStart]!;
	if (mainFirstHeaderSize < 30 || mainFirstHeaderSize > main.bodySize || bytes[main.bodyStart + 6] !== 2) {
		throw new ArchiveError("Invalid ARJ main header");
	}
	const mainFlags = bytes[main.bodyStart + 4]!;
	if ((mainFlags & 0x01) !== 0) throw new ArchiveError("Encrypted ARJ archives are unsupported");
	if ((mainFlags & 0x04) !== 0) throw new ArchiveError("Multi-volume ARJ archives are unsupported");

	const entries: ArchiveIndexEntry[] = [];
	let offset = main.nextOffset;
	let metadataSize = main.metadataSize;
	let parsedCount = 0;
	for (;;) {
		const block = parseArjBlock(bytes, offset, options);
		metadataSize += block.metadataSize;
		assertIndexSize(metadataSize, options.limits, "index");
		if (block.isEnd) break;
		assertEntryCount(++parsedCount, options.limits);
		const firstHeaderSize = bytes[block.bodyStart]!;
		if (firstHeaderSize < 30 || firstHeaderSize > block.bodySize) throw new ArchiveError("Invalid ARJ local header");
		const hostOs = bytes[block.bodyStart + 3]!;
		const flags = bytes[block.bodyStart + 4]!;
		const method = bytes[block.bodyStart + 5]!;
		const fileType = bytes[block.bodyStart + 6]!;
		if ((flags & 0x01) !== 0) throw new ArchiveError("Encrypted ARJ members are unsupported");

View on GitHub (pinned to 9690622007)

Solutions

  1. Rejoin/extract the full volume set with the original ARJ tool or a compatible extractor (e.g. `arj x archive.arj` with all volumes present), then index the resulting single archive or its extracted files.
  2. Ensure all sibling volumes (.arj, .a01, .a02, ...) are present and, if this library only takes one buffer, pre-merge via an external extractor.
  3. Detect this case in your pipeline and reject multi-volume inputs explicitly before calling readArj.
  4. If you create archives, avoid ARJ volume splitting; produce a single archive within your size limits.

Example fix

// before
const entries = await readArj(firstVolumeBytes, options); // throws on multi-volume
// after
if (isMultiVolumeArj(firstVolumeBytes)) {
  const merged = await extractVolumesWithArjTool(volumeDir);
  const entries = await readArj(merged, options);
}
Defensive patterns

Strategy: validation

Validate before calling

function isArjMultiVolume(bytes: Uint8Array): boolean {
	const flags = bytes[mainHeaderBodyStart(bytes) + 4] ?? 0;
	return (flags & 0x04) !== 0;
}
if (isArjMultiVolume(bytes)) throw new Error("Multi-volume ARJ rejected; rejoin volumes first");

Type guard

function isSingleVolumeArjFlags(flags: number): boolean {
	return (flags & 0x04) === 0;
}

Try / catch

try {
	entries = await readArj(source, options);
} catch (e) {
	if (e instanceof ArchiveError && e.message === "Multi-volume ARJ archives are unsupported") {
		return { status: "multi-volume", hint: "extract the full volume set with the ARJ tool first" };
	}
	throw e;
}

Prevention

When it happens

Trigger: readArj is called on the first volume of an ARJ multi-volume set (created with ARJ's '-v' volume options); the main header flags byte at bodyStart+4 has 0x04 set.

Common situations: Old floppy-disk-era backups split into .arj/.a01/.a02 files; downloading only the first part of a multi-part archive; archives re-split by transfer size limits.

Related errors


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