can1357/oh-my-pi · error · ArchiveError

Encrypted ARJ archives are unsupported

Error message

Encrypted ARJ archives are unsupported

What it means

The ARJ reader in readArj validates the flags byte of the archive's main (archive) header. Bit 0 (0x01) of that byte marks the entire archive as password-encrypted. This library does not implement ARJ decryption, so it refuses the archive up front instead of producing unreadable members.

Source

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

	assertInMemorySize(source.size, options.limits);
	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]!;

View on GitHub (pinned to 9690622007)

Solutions

  1. Decrypt the archive with the original ARJ tooling (arj x -g<password>) or an external tool, then feed the plaintext archive to the library.
  2. Obtain the password from the archive's source and pre-process it out of band; this library will not decrypt.
  3. If encrypted archives are expected, detect this error and route to a pipeline/tool that supports ARJ encryption, or reject the input earlier with your own pre-check.
  4. If the archive is NOT actually sensitive and you control creation, regenerate it without the password option.

Example fix

// before
const entries = await readArj(source, options); // throws on password-protected ARJ
// after
if (!hasArjPassword(source)) {
  const entries = await readArj(source, options);
} else {
  const plain = await decryptArjExternally(source, password);
  const entries = await readArj(plain, options);
}
Defensive patterns

Strategy: validation

Validate before calling

function isArjEncrypted(bytes: Uint8Array): boolean {
	// main header located by the same walk readArj performs; at bodyStart:
	// byte 4 is the flags byte, bit 0x01 = encrypted archive
	const flags = bytes[mainHeaderBodyStart(bytes) + 4] ?? 0;
	return (flags & 0x01) !== 0;
}
if (isArjEncrypted(bytes)) throw new Error("Rejecting password-protected ARJ before parse");

Type guard

function isPlaintextArjFlags(flags: number): boolean {
	return (flags & 0x01) === 0;
}

Try / catch

try {
	entries = await readArj(source, options);
} catch (e) {
	if (e instanceof ArchiveError && e.message === "Encrypted ARJ archives are unsupported") {
		return { status: "needs-external-decrypt", archive: path };
	}
	throw e;
}

Prevention

When it happens

Trigger: readArj (the ARJ FormatReader) is called on a buffer whose main header at bodyStart has flags byte (bytes[bodyStart+4]) with bit 0x01 set — i.e. an ARJ archive created with a password ('-g' password option in ARJ tooling).

Common situations: Processing legacy ARJ archives that were password-protected for distribution; archives shared with the password communicated out-of-band (email, chat) that the caller never supplied; automated ingest of old shareware-era archives.

Related errors


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