fix: resolve Windows ENOENT when spawning codex app-server#55
Merged
dkundel-openai merged 2 commits intoopenai:mainfrom Mar 31, 2026
Merged
fix: resolve Windows ENOENT when spawning codex app-server#55dkundel-openai merged 2 commits intoopenai:mainfrom
dkundel-openai merged 2 commits intoopenai:mainfrom
Conversation
On Windows, spawn("codex", ["app-server"]) fails with ENOENT because
Node.js cannot resolve .cmd shims without shell: true. This adds
platform-gated shell and windowsHide options to the app-server spawn
call, and uses terminateProcessTree for proper process tree cleanup
since shell: true wraps the child in cmd.exe.
Without terminateProcessTree, plain SIGTERM only kills cmd.exe and
leaves the actual codex node process orphaned — verified with 274+
zombie node.exe processes accumulating on Windows.
Fixes openai#32
Fixes openai#46
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ChildProcess.killed only reflects whether .kill() was called by this process — it stays false when the child exits on its own. On Windows, where PIDs are recycled quickly, the 50 ms timer could fire after cmd.exe has exited and its PID has been reassigned, causing taskkill to terminate an unrelated process. Add an exitCode === null check so the tree-kill path is skipped once the child has already exited. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Contributor
Author
|
I've added a follow-up commit to guard the delayed Since Adding an if (this.proc && !this.proc.killed && this.proc.exitCode === null) {Verified on Windows 11; local testing confirms that shutdown behavior remains correct. |
dkundel-openai
approved these changes
Mar 31, 2026
This was referenced Mar 31, 2026
This was referenced Apr 7, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
shell: true(Windows-only) andwindowsHide: trueto thespawn("codex", ...)call inapp-server.mjsso Node.js resolves.cmdshims installed by npm.terminateProcessTreeinclose()to kill the entire process tree on Windows, preventing orphanednode.exeprocesses caused byshell: truewrapping the child incmd.exe.windowsHide: trueto the existingspawnSynccall inprocess.mjsto suppress transient console window flashes.Problem
On Windows, npm installs
codexas a global package and exposes it through a.cmdshim (codex.cmd). Whenapp-server.mjscallsspawn("codex", ["app-server"])withoutshell: true, Node.js attempts to executecodexas a raw binary. This fails immediately:This error breaks
/codex:setupand/codex:reviewon Windows. ThespawnSyncpath inprocess.mjswas resolved in PR #13, but the asynchronousspawnpath inapp-server.mjsrequires a similar fix.Solution
plugins/codex/scripts/lib/app-server.mjs(+17/−1)spawn("codex", ["app-server"])to includeshell: process.platform === "win32"andwindowsHide: true. The platform guard leaves macOS and Linux behavior unchanged.close()that callsterminateProcessTree(this.proc.pid)instead ofSIGTERM. Theshell: trueoption makescmd.exethe direct child process; usingSIGTERMterminatescmd.exebut leaves thenode-based codex process orphaned. The call is wrapped in atry/catchblock because it executes inside anunref'd timer during shutdown.plugins/codex/scripts/lib/process.mjs(+2/−1)windowsHide: trueto thespawnSyncoptions. This supplements theshell: process.platform === "win32"configuration from PR fix: resolve ENOENT for .cmd shims on Windows #13 for consistency.Why not just
shell: true?Using
shell: trueresolves theENOENTerror but introduces two secondary issues on Windows:cmd.exe /d /s /c codex app-server. The existingSIGTERMlogic inclose()killscmd.exe, but thenode.exegrandchild survives. Testing showed 274+ orphanednode.exeprocesses accumulating across plugin reloads.terminateProcessTreeusestaskkill /PID <pid> /T /Fto terminate the entire tree.spawncall withshell: truebriefly flashes acmd.execonsole window. ThewindowsHide: trueoption suppresses this usingCREATE_NO_WINDOW.Testing
Verified on Windows 11 running Claude Code with the
codexplugin:/codex:setupspawn codex ENOENT/codex:reviewspawn codex ENOENTnode.execmd.exepopupRelated
Fixes: #32, #46
Related PRs:
shell: trueroot cause; this PR builds on that insight by addingwindowsHideandterminateProcessTree.shell: truefix forspawnSyncinprocess.mjs.