can1357/oh-my-pi · error · ArchiveError

Encrypted ZIP member '${memberPath}' is not supported

Error message

Encrypted ZIP member '${memberPath}' is not supported

What it means

The ZIP member is encrypted: either the general-purpose bit flags carry the encrypted (bit 0) or strong-encryption (bit 6) marker, or the compression method is 99 (AES). This library does not implement ZIP decryption, so it refuses to read the member rather than returning ciphertext.

Source

Thrown at packages/utils/src/ar/zip.ts:393

		flags: number,
		crc: number,
		localHeaderOffset: number,
		limits: ArchiveLimits,
	) {
		this.#source = source;
		this.#compressedSize = compressedSize;
		this.#method = method;
		this.#flags = flags;
		this.#crc = crc;
		this.#localHeaderOffset = localHeaderOffset;
		this.#limits = limits;
	}

	async read(size: number, memberPath: string): Promise<Uint8Array> {
		try {
			assertArchiveMemberSize(Math.max(size, this.#compressedSize), memberPath, this.#limits);
			if ((this.#flags & (ENCRYPTED_FLAG | STRONG_ENCRYPTION_FLAG)) !== 0 || this.#method === 99) {
				throw new ArchiveError(`Encrypted ZIP member '${memberPath}' is not supported`);
			}
			if (SUPPORTED_METHODS[this.#method] !== true) {
				throw new ArchiveError(`Unsupported ZIP compression method ${this.#method} for '${memberPath}'`);
			}
			const headerEnd = checkedEnd(
				this.#localHeaderOffset,
				30,
				this.#source.size,
				`local header for '${memberPath}'`,
			);
			const header = await this.#source.read(this.#localHeaderOffset, headerEnd);
			if (header.byteLength !== 30 || readUInt32LE(header, 0) !== LOCAL_HEADER_SIGNATURE) {
				throw new ArchiveError(`Invalid ZIP archive: malformed local header for '${memberPath}'`);
			}
			const localFlags = readUInt16LE(header, 6);
			if ((localFlags & (ENCRYPTED_FLAG | STRONG_ENCRYPTION_FLAG)) !== 0) {
				throw new ArchiveError(`Encrypted ZIP member '${memberPath}' is not supported`);
			}

View on GitHub (pinned to 9690622007)

Solutions

  1. Decrypt the archive first with an external tool: 7z x -p<password> archive.zip or unzip -P <password> (ZipCrypto only).
  2. Recreate the archive without encryption if the password is unnecessary (zip -e removed; plain zip -r).
  3. If you control the workflow, encrypt at the transport/storage layer (age, openssl enc) instead of inside the ZIP so the library can read members.
  4. If you need in-library decryption, decrypt via a separate step and feed the plaintext archive to this parser.

Example fix

// before
// const data = await zip.read('secret.txt'); // throws: encrypted
// after
// await $`7z x -p${password} -o${outdir} archive.zip`.quiet().nothrow();
// const data = await Bun.file(`${outdir}/secret.txt`).bytes();
Defensive patterns

Strategy: try-catch

Validate before calling

import { $ } from "bun";
const res = await $`7z l -slt archive.zip`.quiet().nothrow();
if (res.exitCode === 0 && (await res.text()).includes("Encrypted = +")) {
  throw new Error("archive is encrypted; prompt for password before reading");
}

Try / catch

try {
  return await archive.readMember(path);
} catch (err) {
  if (err instanceof ArchiveError && err.message.startsWith("Encrypted ZIP member")) {
    // decrypt via external tool with user-supplied password, then retry
    await $`7z x -p${password} -o${outdir} archive.zip`.quiet().nothrow();
    return await Bun.file(`${outdir}/${path}`).bytes();
  } else throw err;
}

Prevention

When it happens

Trigger: Calling read/extract on a member whose central-directory flags include ENCRYPTED_FLAG (0x1) or STRONG_ENCRYPTION_FLAG (0x40), or whose method is 99 (AES encryption).

Common situations: Password-protected ZIPs created by WinZip/7-Zip with AES-256, legacy ZipCrypto archives, secure export pipelines that encrypt archives by default.

Related errors


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