can1357/oh-my-pi · error · ToolError

Path is not a directory: ${target.searchPath}

Error message

Path is not a directory: ${target.searchPath}

What it means

After stat succeeds, execute() verifies the target is a directory (unless the glob itself matched a file). A single-target call whose searchPath is a regular file, socket, fifo, etc. throws 'Path is not a directory' because recursive globbing requires a directory root.

Source

Thrown at packages/coding-agent/src/tools/glob.ts:434

					try {
						stat = await this.#stat(target.searchPath);
					} catch (err) {
						// ENAMETOOLONG can never name a real target; surface a clean
						// "Path not found" instead of leaking the raw errno (issue #7597).
						if (isEnoent(err) || hasFsCode(err, "ENAMETOOLONG")) {
							if (isSingle) throw new ToolError(`Path not found: ${scopePath}`);
							return { target, result: [] };
						}
						throw err;
					}
					if (!target.hasGlob && stat.isFile()) {
						return {
							target,
							result: [{ path: formatScopePath(target.searchPath), mtime: stat.mtimeMs }],
						};
					}
					if (!stat.isDirectory()) {
						if (isSingle) throw new ToolError(`Path is not a directory: ${target.searchPath}`);
						return { target, result: [] };
					}
					return { target };
				}),
			);
			const nativeScanPending = preparedTargets.some(prepared => prepared.result === undefined);
			if (nativeScanPending && preparationController && signal) {
				signal.removeEventListener("abort", abortPreparation);
			}
			throwIfAborted(signal);

			const onUpdateMatches: string[] = [];
			const onUpdateMtimes: number[] = [];
			const updateIntervalMs = 200;
			let lastUpdate = 0;
			const emitUpdate = () => {
				if (!onUpdate) return;
				const now = Date.now();

View on GitHub (pinned to 9690622007)

Solutions

  1. Pass a directory as the search path
  2. If you intend a single file, omit the glob pattern so the file-match branch returns it directly
  3. Check fs stat isDirectory() before calling

Example fix

// before
await globTool.execute({ pattern: "**/*.ts", path: "src/index.ts" })
// after
await globTool.execute({ pattern: "**/*.ts", path: "src" })
Defensive patterns

Strategy: validation

Validate before calling

const st = await fs.stat(p);
if (!st.isDirectory()) throw new Error(`Expected a directory: ${p}`);
await globTool.execute({ pattern, path: p });

Type guard

async function isDirectory(p: string): Promise<boolean> {
  return (await fs.stat(p).catch(() => null))?.isDirectory() ?? false;
}

Try / catch

try {
  return await globTool.execute({ pattern, path });
} catch (err) {
  if (err instanceof ToolError && err.message.startsWith("Path is not a directory:")) {
    // treat as single-file match or fix the caller's path variable
  }
  throw err;
}

Prevention

When it happens

Trigger: Passing a file path as the glob search root with a pattern that does not match that exact file; passing /dev/null, a symlink to a file, or a special file.

Common situations: Confusing the glob tool's signature with grep-like tools that accept files; variable holding a file path reused where a directory was expected; symlink targets changed.

Related errors


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