can1357/oh-my-pi · error · ToolError
Working directory does not exist: ${commandCwd}
Error message
Working directory does not exist: ${commandCwd} What it means
Before running a command the tool stats the resolved working directory (the cwd parameter resolved against the session cwd, or the session cwd itself). If stat fails with ENOENT the path does not exist, so there is nowhere to run the command and the tool throws this ToolError. Non-ENOENT stat errors are re-thrown unchanged.
Source
Thrown at packages/coding-agent/src/tools/bash.ts:990
// Resolve protocol URLs (skill://, agent://, etc.) in extracted cwd.
if (cwd?.includes("://") || cwd?.includes("local:/")) {
cwd = await expandInternalUrls(cwd, { ...internalUrlOptions, noEscape: true });
}
// 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);View on GitHub (pinned to 9690622007)
Solutions
- Verify the path exists (ls the parent directory) and fix typos before re-running.
- Create the directory first if it is supposed to exist, or drop the cwd parameter to use the session cwd.
- If the session cwd vanished, restart/re-root the session in an existing directory.
Example fix
// before: cwd was never created
await bash.execute(id, { command: "ls", cwd: "/tmp/build-42/output" });
// after: ensure it exists first
await fs.mkdir("/tmp/build-42/output", { recursive: true });
await bash.execute(id, { command: "ls", cwd: "/tmp/build-42/output" }); Defensive patterns
Strategy: validation
Validate before calling
import { stat } from "node:fs/promises";
const dir = resolveToCwd(cwd, session.cwd);
try {
if (!(await stat(dir)).isDirectory()) throw new Error(`not a directory: ${dir}`);
} catch (e) {
if (e.code === "ENOENT") throw new Error(`path does not exist: ${dir}`);
throw e;
}
await bash.execute(id, { command, cwd: dir }); Type guard
function isEnoentErr(e: unknown): e is NodeJS.ErrnoException & { code: "ENOENT" } {
return typeof e === "object" && e !== null && "code" in e && (e as NodeJS.ErrnoException).code === "ENOENT";
} Try / catch
try {
await bash.execute(id, { command, cwd });
} catch (e) {
if (e instanceof ToolError && e.message.startsWith("Working directory does not exist")) {
await fs.mkdir(cwd, { recursive: true });
return bash.execute(id, { command, cwd });
}
throw e;
} Prevention
- Stat the target directory before each bash call with a custom cwd.
- Prefer the session cwd (omit cwd) unless a specific directory is required.
- Create output directories with mkdir recursive before referencing them.
- Watch for directories deleted by earlier commands in the same session.
When it happens
Trigger: Passing cwd=/path/that/does/not/exist to the bash tool; a leading `cd <missing-dir> && ...` in the command; the session cwd having been deleted while the session was open (e.g. tmpdir cleanup, branch switch removing a directory).
Common situations: Typo'd or partially-typed absolute paths; directories removed by an earlier command in the same session; agents constructing paths from stale listings; temp directories cleaned between commands.
Related errors
- Working directory is not a directory: ${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/3373a2daa3d9ae0b.
Report an issue: GitHub.