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
- Pass a directory as the search path
- If you intend a single file, omit the glob pattern so the file-match branch returns it directly
- 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
- Confirm with fs.stat().isDirectory() before using a path as a glob root
- Don't reuse file-path variables for directory parameters
- Remember: to glob a single file, omit the pattern rather than pointing the root at the file
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
- Managed skill "${name}" SKILL.md is not a regular file; refu
- Shared-folder destination escapes its configured root
- Path is not a file: ${pathArg}
- local:// URL must resolve to a file or directory: ${url.href
- File not found: ${filePath}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/3a77c243edfa2499.
Report an issue: GitHub.