can1357/oh-my-pi · error · ArchiveError

Encrypted RAR5 member '${rawPath}' is not supported

Error message

Encrypted RAR5 member '${rawPath}' is not supported

What it means

RAR5 file headers may carry an extra record of type 1 (FILE_ENCRYPTION) marking the member as password-encrypted. The library does not implement RAR decryption, so any encrypted member is rejected, with the member name included in the message.

Source

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

			if ((compression & 0x3f) === 1) dictionarySize += (dictionarySize * ((compression >>> 15) & 0x1f)) / 32;
			if (!Number.isSafeInteger(dictionarySize) || dictionarySize > options.limits.maxInMemorySize) {
				throw new ArchiveError(`RAR5 dictionary is too large (${dictionarySize} bytes)`);
			}
			const hostOs = readVint(bytes, cursor, extraStart, "host OS");
			const nameSize = readVint(bytes, cursor, extraStart, "file name size");
			assertArchivePathBytes(nameSize, "member path", options.limits.maxPathBytes);
			need(cursor.offset, nameSize, extraStart, "RAR5 file name");
			let rawPath: string;
			try {
				rawPath = UTF8.decode(bytes.subarray(cursor.offset, cursor.offset + nameSize));
			} catch {
				throw new ArchiveError("Invalid RAR5 UTF-8 member name");
			}
			cursor.offset += nameSize;
			let linkTarget: string | undefined;
			for (const extra of readExtraRecords(bytes, extraStart, headerEnd)) {
				const extraCursor = { offset: extra.start };
				if (extra.type === 1) throw new ArchiveError(`Encrypted RAR5 member '${rawPath}' is not supported`);
				if (extra.type === 3) {
					const timeFlags = readVint(bytes, extraCursor, extra.end, "time flags");
					if ((timeFlags & 2) !== 0) {
						if ((timeFlags & 1) !== 0) {
							need(extraCursor.offset, 4, extra.end, "Unix modification time");
							mtimeMs = readUInt32LE(bytes, extraCursor.offset) * 1000;
						} else {
							need(extraCursor.offset, 8, extra.end, "Windows modification time");
							mtimeMs = filetimeMs(bytes, extraCursor.offset);
						}
					}
				} else if (extra.type === 5) {
					const redirectionType = readVint(bytes, extraCursor, extra.end, "redirection type");
					readVint(bytes, extraCursor, extra.end, "redirection flags");
					const targetSize = readVint(bytes, extraCursor, extra.end, "link target size");
					assertArchivePathBytes(targetSize, "link target", options.limits.maxPathBytes);
					need(extraCursor.offset, targetSize, extra.end, "link target");
					if (redirectionType < 1 || redirectionType > 5)

View on GitHub (pinned to 9690622007)

Solutions

  1. Decrypt first with the unrar CLI (`unrar x -p<password>`) and parse the plaintext output
  2. Ask the archive producer to create an unencrypted copy
  3. Detect encrypted members upstream and prompt for a password before invoking this library

Example fix

// before
const records = readRar(await Bun.file('locked.rar').bytes()); // throws on encrypted member
// after
await $`unrar x -p${password} locked.rar extracted/`;
const records = readRar(await Bun.file('extracted/plain.rar').bytes());
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const records = readRar(bytes);
} catch (err) {
  if (err instanceof ArchiveError && /Encrypted RAR5 member/.test(err.message)) {
    const member = /'(.+)'/.exec(err.message)?.[1];
    // prompt for password and extract via unrar
  } else throw err;
}

Prevention

When it happens

Trigger: Parsing a RAR5 archive where at least one file entry has the encryption extra record — i.e. the archive was password-protected without filename encryption (per-file encryption also occurs with -hp).

Common situations: User-uploaded password-protected archives; automated pipelines that cannot supply passwords; archives shared internally with a password for confidentiality.

Related errors


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