can1357/oh-my-pi · error · ToolError
Working directory is not a directory: ${commandCwd}
Error message
Working directory is not a directory: ${commandCwd} What it means
After a successful stat of the resolved working directory, the tool checks isDirectory(). If the path exists but is a file, symlink-to-file, socket, etc., it cannot serve as a cwd and the tool throws this ToolError. This complements the ENOENT check: the path is real but the wrong kind of node.
Source
Thrown at packages/coding-agent/src/tools/bash.ts:995
// Best-effort cache invalidation: drop github-cache rows for any issue/PR
// number touched by a mutating `gh` subcommand inside this bash call so
// subsequent issue:// / pr:// reads pick up the post-mutation state
// instead of the cached pre-mutation snapshot.
invalidateGithubCacheForBashCommand(command);
const commandCwd = cwd ? resolveToCwd(cwd, this.session.cwd) : this.session.cwd;
let cwdStat: fs.Stats;
try {
cwdStat = await fs.promises.stat(commandCwd);
} catch (err) {
if (isEnoent(err)) {
throw new ToolError(`Working directory does not exist: ${commandCwd}`);
}
throw err;
}
if (!cwdStat.isDirectory()) {
throw new ToolError(`Working directory is not a directory: ${commandCwd}`);
}
// A timeout of 0 is an explicit long-running-command contract: the user
// must still cancel the call or job, but OMP does not impose a deadline.
const requestedTimeoutSec = rawTimeout;
const timeoutDisabled = requestedTimeoutSec === 0;
const maxTimeout = this.session.settings.get("tools.maxTimeout");
const timeoutSec = timeoutDisabled ? undefined : clampTimeout("bash", requestedTimeoutSec, maxTimeout);
const timeoutMs = timeoutSec === undefined ? undefined : timeoutSec * 1000;
const pendingNotices: string[] = [];
if (timeoutSec !== undefined) {
const timeoutClampNotice = formatTimeoutClampNotice(requestedTimeoutSec, timeoutSec, maxTimeout);
if (timeoutClampNotice) pendingNotices.push(timeoutClampNotice);
}
if (asyncRequested) {
if (!this.session.asyncJobManager) {
throw new ToolError("Async job manager unavailable for this session.");View on GitHub (pinned to 9690622007)
Solutions
- Point cwd at the directory, not a file inside it (strip the filename or use its dirname).
- Verify with `ls -la <path>` / stat whether the target is a directory or file.
- If a symlink flipped type unexpectedly, inspect what replaced it and re-clone/restore the directory.
Example fix
// before: cwd is a file
await bash.execute(id, { command: "npm test", cwd: "/repo/packages/app/package.json" });
// after: use the containing directory
await bash.execute(id, { command: "npm test", cwd: "/repo/packages/app" }); Defensive patterns
Strategy: validation
Validate before calling
const dir = cwd ?? session.cwd;
if ((await stat(dir)).isDirectory()) {
await bash.execute(id, { command, cwd: dir });
} Type guard
function isDirectoryStat(s: fs.Stats): boolean {
return s.isDirectory();
} Try / catch
try {
await bash.execute(id, { command, cwd });
} catch (e) {
if (e instanceof ToolError && e.message.includes("is not a directory")) {
const dir = path.dirname(cwd!);
return bash.execute(id, { command, cwd: dir });
}
throw e;
} Prevention
- Pass directories, never files, as cwd — strip filenames with path.dirname.
- Verify symlink targets still resolve to directories before use.
- Check `ls -la` output when paths were created by other tooling.
When it happens
Trigger: Passing cwd pointing at a regular file (e.g. /path/to/package.json instead of the package dir); a leading `cd <file> && ...` where the token resolves to a file; a path that used to be a directory but was replaced by a file.
Common situations: Agents copying a file path into the cwd field by mistake; confusion between a repo path and its config file; symlink targets changed by tooling.
Related errors
- Working directory does not exist: ${commandCwd}
- cannot access {}: Not a directory
- failed to access {0}: Not a directory
- Not a file: ${shortenPath(resolved)}
- File not found: ${path}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/22f6f47554ca4bf4.
Report an issue: GitHub.