can1357/oh-my-pi · error · ArchiveError

Multi-volume ARJ members are unsupported

Error message

Multi-volume ARJ members are unsupported

What it means

Bits 2-3 (0x0c) of an ARJ local file header's flags byte mark the member as part of a multi-volume sequence (continued on another volume, in various directions). The single-buffer reader cannot assemble members spanning volumes, so it rejects archives containing such members.

Source

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

	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");
		if ((flags & 0x0c) !== 0) throw new ArchiveError("Multi-volume ARJ members are unsupported");
		const packedSize = u32(bytes, block.bodyStart + 12);
		const size = u32(bytes, block.bodyStart + 16);
		const fileCrc = u32(bytes, block.bodyStart + 20);
		const accessMode = u16(bytes, block.bodyStart + 26);
		const filename = readCString(
			bytes,
			block.bodyStart + firstHeaderSize,
			block.bodyStart + block.bodySize,
			"filename",
		);
		readCString(bytes, filename.next, block.bodyStart + block.bodySize, "comment");
		assertArchivePathBytes(
			filename.next - (block.bodyStart + firstHeaderSize) - 1,
			"member path",
			options.limits.maxPathBytes,
		);
		const rawPath = normalizeHostPath(filename.value, hostOs);
		assertArchivePathString(rawPath, "member path", options.limits.maxPathBytes);

View on GitHub (pinned to 9690622007)

Solutions

  1. Extract the complete volume set with the original ARJ tool (all .arj/.aNN files present) and index the extracted files or the re-merged single archive.
  2. Reject multi-volume inputs in your pipeline before parsing by pre-checking the main header flags (0x04) and member flags.
  3. Repackage the data as a single-volume archive if you control the source.
  4. Use an external ARJ-compatible extractor that supports volumes, then hand the plain files to this library's other readers if applicable.

Example fix

// before
const entries = await readArj(volume1Bytes, options); // throws on spanning member
// after
if (hasArjVolumeFlags(volume1Bytes)) {
  const files = await extractAllVolumesWithArjTool(volumeDir);
  // index extracted plain files instead
}
Defensive patterns

Strategy: validation

Validate before calling

function hasArjVolumeMembers(bytes: Uint8Array): boolean {
	return scanArjLocalFlags(bytes).some((flags) => (flags & 0x0c) !== 0);
}
if (hasArjVolumeMembers(bytes)) throw new Error("ARJ members span volumes; extract the full set first");

Type guard

function isSingleVolumeArjMemberFlags(flags: number): boolean {
	return (flags & 0x0c) === 0;
}

Try / catch

try {
	entries = await readArj(source, options);
} catch (e) {
	if (e instanceof ArchiveError && e.message === "Multi-volume ARJ members are unsupported") {
		return { status: "multi-volume-member", hint: "extract all volumes with the ARJ tool, then index the output" };
	}
	throw e;
}

Prevention

When it happens

Trigger: readArj parses a local header whose flags byte has any of 0x04 or 0x08 set — the member's data continues on the next/previous volume of a split archive.

Common situations: Members split across .arj/.a01/.a02 volumes of a floppy-era backup; indexing only the first volume while the member actually spans volumes.

Related errors


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