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
- Inspect `<asar>.unpacked/<memberPath>` and fix it to be a regular file with the expected contents
- Rebuild the .unpacked directory from a clean unpack of the original ASAR
- 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
- Rebuild .unpacked from a clean unpack rather than hand-crafting it
- Validate sidecar layout after copying bundles
- Watch for path collisions between files and directories with the same name
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
- Archive file '${label}' is unpacked and requires a filesyste
- Unpacked ASAR file '${label}' was not found
- cannot move {0} to a subdirectory of itself, {1}
- Cannot resolve ${this.#createScope} agents directory.
- Could not find agent compaction prompts
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/360eb282cbd28a48.
Report an issue: GitHub.