{"record":{"id":"3373a2daa3d9ae0b","repo":"can1357/oh-my-pi","slug":"working-directory-does-not-exist-commandcwd","errorCode":null,"errorMessage":"Working directory does not exist: ${commandCwd}","messagePattern":"Working directory does not exist: (.+?)","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/tools/bash.ts","lineNumber":990,"sourceCode":"\n\t\t// Resolve protocol URLs (skill://, agent://, etc.) in extracted cwd.\n\t\tif (cwd?.includes(\"://\") || cwd?.includes(\"local:/\")) {\n\t\t\tcwd = await expandInternalUrls(cwd, { ...internalUrlOptions, noEscape: true });\n\t\t}\n\n\t\t// Best-effort cache invalidation: drop github-cache rows for any issue/PR\n\t\t// number touched by a mutating `gh` subcommand inside this bash call so\n\t\t// subsequent issue:// / pr:// reads pick up the post-mutation state\n\t\t// instead of the cached pre-mutation snapshot.\n\t\tinvalidateGithubCacheForBashCommand(command);\n\n\t\tconst commandCwd = cwd ? resolveToCwd(cwd, this.session.cwd) : this.session.cwd;\n\t\tlet cwdStat: fs.Stats;\n\t\ttry {\n\t\t\tcwdStat = await fs.promises.stat(commandCwd);\n\t\t} catch (err) {\n\t\t\tif (isEnoent(err)) {\n\t\t\t\tthrow new ToolError(`Working directory does not exist: ${commandCwd}`);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t\tif (!cwdStat.isDirectory()) {\n\t\t\tthrow new ToolError(`Working directory is not a directory: ${commandCwd}`);\n\t\t}\n\n\t\t// A timeout of 0 is an explicit long-running-command contract: the user\n\t\t// must still cancel the call or job, but OMP does not impose a deadline.\n\t\tconst requestedTimeoutSec = rawTimeout;\n\t\tconst timeoutDisabled = requestedTimeoutSec === 0;\n\t\tconst maxTimeout = this.session.settings.get(\"tools.maxTimeout\");\n\t\tconst timeoutSec = timeoutDisabled ? undefined : clampTimeout(\"bash\", requestedTimeoutSec, maxTimeout);\n\t\tconst timeoutMs = timeoutSec === undefined ? undefined : timeoutSec * 1000;\n\t\tconst pendingNotices: string[] = [];\n\t\tif (timeoutSec !== undefined) {\n\t\t\tconst timeoutClampNotice = formatTimeoutClampNotice(requestedTimeoutSec, timeoutSec, maxTimeout);\n\t\t\tif (timeoutClampNotice) pendingNotices.push(timeoutClampNotice);","sourceCodeStart":972,"sourceCodeEnd":1008,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/tools/bash.ts#L972-L1008","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: cwd was never created\nawait bash.execute(id, { command: \"ls\", cwd: \"/tmp/build-42/output\" });\n\n// after: ensure it exists first\nawait fs.mkdir(\"/tmp/build-42/output\", { recursive: true });\nawait bash.execute(id, { command: \"ls\", cwd: \"/tmp/build-42/output\" });","handlingStrategy":"validation","validationCode":"import { stat } from \"node:fs/promises\";\nconst dir = resolveToCwd(cwd, session.cwd);\ntry {\n  if (!(await stat(dir)).isDirectory()) throw new Error(`not a directory: ${dir}`);\n} catch (e) {\n  if (e.code === \"ENOENT\") throw new Error(`path does not exist: ${dir}`);\n  throw e;\n}\nawait bash.execute(id, { command, cwd: dir });","typeGuard":"function isEnoentErr(e: unknown): e is NodeJS.ErrnoException & { code: \"ENOENT\" } {\n  return typeof e === \"object\" && e !== null && \"code\" in e && (e as NodeJS.ErrnoException).code === \"ENOENT\";\n}","tryCatchPattern":"try {\n  await bash.execute(id, { command, cwd });\n} catch (e) {\n  if (e instanceof ToolError && e.message.startsWith(\"Working directory does not exist\")) {\n    await fs.mkdir(cwd, { recursive: true });\n    return bash.execute(id, { command, cwd });\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["filesystem","path","cwd"],"backgroundTag":"no-such-file-or-directory","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}