can1357/oh-my-pi · error · ArchiveError

Archive path '${normalizedPath}' is not a directory

Error message

Archive path '${normalizedPath}' is not a directory

What it means

`listDirectory` found an entry at the resolved path, but it is a regular file (or other non-directory member), so it throws ArchiveError(`Archive path '${normalizedPath}' is not a directory`). Directory listing only works on entries flagged isDirectory (packages/utils/src/ar/reader.ts:88).

Source

Thrown at packages/utils/src/ar/reader.ts:88

	}

	/** List one directory's children, sorted case-insensitively by name. */
	listDirectory(subPath?: string): ArchiveDirectoryEntry[] {
		const normalizedPath = normalizeArchiveLookupPath(subPath);
		if (normalizedPath === undefined) {
			throw new ArchiveError("Archive path cannot contain '..'");
		}

		const resolvedPath = normalizedPath
			? resolveArchiveLinkPath(this.#entries, normalizedPath, this.limits.maxLinkDepth)
			: "";
		if (normalizedPath && resolvedPath !== "") {
			const entry = this.#entries.get(resolvedPath);
			if (!entry) {
				throw new ArchiveError(`Archive path '${normalizedPath}' not found`);
			}
			if (!entry.isDirectory) {
				throw new ArchiveError(`Archive path '${normalizedPath}' is not a directory`);
			}
		}

		const sourcePrefix = resolvedPath ? `${resolvedPath}/` : "";
		const children = new Map<string, ArchiveDirectoryEntry>();

		for (const entry of this.#entries.values()) {
			if (resolvedPath) {
				if (!entry.path.startsWith(sourcePrefix) || entry.path === resolvedPath) continue;
			}

			const relativePath = resolvedPath ? entry.path.slice(sourcePrefix.length) : entry.path;
			const nextSegment = relativePath.split("/")[0];
			if (!nextSegment) continue;

			const childPath = normalizedPath ? `${normalizedPath}/${nextSegment}` : nextSegment;
			if (children.has(childPath)) continue;

View on GitHub (pinned to 9690622007)

Solutions

  1. Check `entry.isDirectory` from allEntries() before calling listDirectory; use readFile for file entries instead.
  2. If you want the parent folder of a file, derive it (strip the last path segment) and list that directory.
  3. Catch this ArchiveError and dispatch to readFile when the path turns out to be a file.
  4. For symlink paths, resolve intent first: inspect the entry's target and its isDirectory flag.

Example fix

// before: same handler for any path
const kids = reader.listDirectory(targetPath);

// after: branch on entry type
const entry = reader.allEntries().find(e => e.path === targetPath);
if (entry && !entry.isDirectory) {
  const file = await reader.readFile(targetPath);
} else {
  const kids = reader.listDirectory(targetPath);
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Check entry kind before choosing the API
const entry = reader.allEntries().find(e => e.path === targetPath);
if (entry && !entry.isDirectory) {
  await reader.readFile(targetPath); // file path
} else {
  reader.listDirectory(targetPath); // directory path
}

Type guard

function isFileEntry(
  entries: { path: string; isDirectory: boolean }[],
  p: string,
): boolean {
  const e = entries.find(x => x.path === p);
  return e !== undefined && !e.isDirectory;
}

Try / catch

try {
  return reader.listDirectory(p);
} catch (err) {
  if (err instanceof ArchiveError && err.message.endsWith('is not a directory')) {
    return { kind: 'file' as const, read: () => reader.readFile(p) };
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling `reader.listDirectory('file.txt')` where that path maps to a file entry; passing a symlink that resolves to a file instead of a directory; confusing the file/directory listing APIs (readFile vs listDirectory) with the same path.

Common situations: Code that walks archives generically and calls listDirectory on every entry without checking isDirectory; paths that look like directories because of extensions but are files; API misuse where readFile should have been used.

Related errors


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