can1357/oh-my-pi · error · ArchiveError

Unsupported multi-volume RAR4 member

Error message

Unsupported multi-volume RAR4 member

What it means

For RAR4 file headers (type 0x74), flag bits 0 and 1 mark a member as continued from the previous volume / continued in the next volume. Such a member's data is split across volume files, which this parser cannot assemble, so it throws ArchiveError. This is the per-member variant of multi-volume rejection.

Source

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

			const dosTime = readUInt32LE(bytes, cursor);
			cursor += 4;
			const version = bytes[cursor++]!;
			const methodByte = bytes[cursor++]!;
			const nameSize = readUInt16LE(bytes, cursor);
			cursor += 2;
			const attributes = readUInt32LE(bytes, cursor);
			cursor += 4;
			let packedSize = dataSize;
			let unpackedSize = unpackedLow;
			if ((flags & 0x100) !== 0) {
				need(cursor, 8, headerEnd, "RAR4 high sizes");
				packedSize += readUInt32LE(bytes, cursor) * 0x100000000;
				cursor += 4;
				unpackedSize += readUInt32LE(bytes, cursor) * 0x100000000;
				cursor += 4;
				dataEnd = checkedEnd(dataStart, packedSize, bytes.byteLength, "RAR4 file data");
			}
			if ((flags & 3) !== 0) throw new ArchiveError("Unsupported multi-volume RAR4 member");
			if ((flags & 4) !== 0) throw new ArchiveError("Encrypted RAR4 file data is not supported");
			if (methodByte < 0x30 || methodByte > 0x35)
				throw new ArchiveError(`Unsupported RAR4 compression method 0x${methodByte.toString(16)}`);
			assertArchivePathBytes(nameSize, "member path", options.limits.maxPathBytes);
			need(cursor, nameSize, headerEnd, "RAR4 file name");
			const nameBytes = bytes.subarray(cursor, cursor + nameSize);
			const rawPath = (flags & 0x200) !== 0 ? decodeRar4UnicodeName(nameBytes) : LATIN1.decode(nameBytes);
			cursor += nameSize;
			if ((flags & 0x400) !== 0) {
				need(cursor, 8, headerEnd, "RAR4 salt");
				cursor += 8;
			}
			let mtimeMs = dosTimeMs(dosTime);
			if ((flags & 0x1000) !== 0) {
				need(cursor, 2, headerEnd, "RAR4 extended time flags");
				const timeFlags = readUInt16LE(bytes, cursor);
				cursor += 2;
				for (let timeIndex = 0; timeIndex < 4; timeIndex++) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Merge/extract the full volume set with unrar or 7-Zip and consume the resulting single files.
  2. Re-create the archive as one volume without -v.
  3. Repackage as zip/tar before parsing.
  4. Catch ArchiveError to detect split members and request all volumes.

Example fix

// before
await records(Bun.file("data.r00")) // member split across volumes
// after: extract full set first
$`unrar x -y data.part1.rar out/`
Defensive patterns

Strategy: try-catch

Validate before calling

import { readdirSync } from "node:fs";
const parts = readdirSync(dir).filter(f => /\.rar$|\.r\d{2}$|\.part\d+\.rar$/i.test(f));
if (parts.length > 1) throw new Error("volume set — merge before parsing");

Try / catch

try {
  const recs = await records(file);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("multi-volume RAR4 member")) {
    // merge volumes externally, re-run parse on merged output
  } else throw err;
}

Prevention

When it happens

Trigger: Calling records() on any volume of a split RAR4 set — members with the split flags trigger this even if the main-header volume flag check passed (e.g. hand-edited or partial sets).

Common situations: Reading volume .part2/.r00 files directly; archives split after creation with a splitter that corrupted header flags; legacy multi-disk archives.

Related errors


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