diff --git a/apps/server/src/codexAppServerManager.test.ts b/apps/server/src/codexAppServerManager.test.ts index ab3b7a569d..0f89ae6fe4 100644 --- a/apps/server/src/codexAppServerManager.test.ts +++ b/apps/server/src/codexAppServerManager.test.ts @@ -470,6 +470,35 @@ describe("startSession", () => { manager.stopAll(); } }); + + it("reports missing working directory instead of missing binary when cwd does not exist", async () => { + const manager = new CodexAppServerManager(); + const events: Array<{ method: string; kind: string; message?: string }> = []; + manager.on("event", (event) => { + events.push({ + method: event.method, + kind: event.kind, + ...(event.message ? { message: event.message } : {}), + }); + }); + + const missingCwd = path.join(os.tmpdir(), `t3code-nonexistent-${randomUUID()}`); + try { + await expect( + manager.startSession({ + threadId: asThreadId("thread-cwd-missing"), + provider: "codex", + binaryPath: "codex", + cwd: missingCwd, + runtimeMode: "full-access", + }), + ).rejects.toThrow(/Working directory.*does not exist/); + expect(events).toHaveLength(1); + expect(events[0]?.method).toBe("session/startFailed"); + } finally { + manager.stopAll(); + } + }); }); describe("sendTurn", () => { diff --git a/apps/server/src/codexAppServerManager.ts b/apps/server/src/codexAppServerManager.ts index 230ba8e364..e6c5123ce9 100644 --- a/apps/server/src/codexAppServerManager.ts +++ b/apps/server/src/codexAppServerManager.ts @@ -1,6 +1,7 @@ import { type ChildProcessWithoutNullStreams, spawn, spawnSync } from "node:child_process"; import { randomUUID } from "node:crypto"; import { EventEmitter } from "node:events"; +import fs from "node:fs"; import readline from "node:readline"; import { @@ -1553,6 +1554,14 @@ function assertSupportedCodexCliVersion(input: { lower.includes("command not found") || lower.includes("not found") ) { + // Disambiguate: ENOENT can mean the binary is missing OR the cwd + // does not exist. Check the cwd in the error path so we surface + // the right message without a racy preflight. + try { + fs.accessSync(input.cwd); + } catch { + throw new Error(`Working directory '${input.cwd}' does not exist or is not accessible.`); + } throw new Error(`Codex CLI (${input.binaryPath}) is not installed or not executable.`); } throw new Error(