can1357/oh-my-pi · error · ArchiveError

Unpacked ASAR file '${label}' is a directory

Error message

Unpacked ASAR file '${label}' is a directory

What it means

Thrown when the stat of the unpacked sidecar path resolves to a directory rather than a regular file. The ASAR header declared a file member, but on disk the same path is a directory (e.g. a directory was unpacked as if it were a file, or path resolution landed on a folder).

Source

Thrown at packages/utils/src/ar/asar.ts:160

		this.#filePath = filePath;
		this.#size = size;
		this.#integrity = integrity;
	}

	async read(size: number, memberPath: string): Promise<Uint8Array> {
		const label = formatArchivePathForError(memberPath);
		if (size !== this.#size) {
			throw new ArchiveError(`ASAR member '${label}' has an inconsistent size`);
		}
		if (!this.#filePath) {
			throw new ArchiveError(`Archive file '${label}' is unpacked and requires a filesystem-backed ASAR archive`);
		}
		const file = Bun.file(this.#filePath);
		const stat = await file.stat().catch(() => {
			throw new ArchiveError(`Unpacked ASAR file '${label}' was not found`);
		});
		if (stat.isDirectory()) {
			throw new ArchiveError(`Unpacked ASAR file '${label}' is a directory`);
		}
		if (stat.size !== this.#size) {
			throw new ArchiveError(
				`Unpacked ASAR file '${label}' size differs from its archive header (${stat.size} != ${this.#size} bytes)`,
			);
		}
		let bytes: Uint8Array;
		try {
			bytes = await file.bytes();
		} catch (error) {
			throw new ArchiveError(`Failed to read unpacked ASAR file '${label}': ${describeError(error)}`);
		}
		if (bytes.byteLength !== this.#size) {
			throw new ArchiveError(`Unpacked ASAR file '${label}' changed while being read`);
		}
		verifyIntegrity(bytes, this.#integrity, memberPath);
		return bytes;
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect `<asar>.unpacked/<memberPath>` and fix it to be a regular file with the expected contents
  2. Rebuild the .unpacked directory from a clean unpack of the original ASAR
  3. Check the ASAR header entry — if the path should be a directory, the packer that produced the header is wrong
Defensive patterns

Strategy: validation

Validate before calling

const st = await fs.stat(sidecar);
if (!st.isFile()) throw new Error(`${sidecar} is not a regular file`);

Type guard

const isRegularFile = (st) => st.isFile();

Try / catch

try { return await readMember(entry); } catch (e) { if (e instanceof ArchiveError && e.message.includes("is a directory")) { /* fix or re-extract the .unpacked tree */ } throw e; }

Prevention

When it happens

Trigger: `<archive>.unpacked/<memberPath>` exists as a directory; a member name collides with a directory in the sidecar layout; broken repack tooling produced a directory at a file path.

Common situations: Hand-built .unpacked directories with wrong structure; symlinks pointing at directories; merging app bundles where a file was replaced by a folder (e.g. path 'foo' vs 'foo/bar').

Related errors


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