Skip to content

fix: Windows SHELL env var breaks taskkill; handleCancel aborts before updating job state on a partial kill failure - #656

Open
mittalpk wants to merge 1 commit into
openai:mainfrom
mittalpk:fix/windows-shell-env-and-cancel-guard
Open

fix: Windows SHELL env var breaks taskkill; handleCancel aborts before updating job state on a partial kill failure#656
mittalpk wants to merge 1 commit into
openai:mainfrom
mittalpk:fix/windows-shell-env-and-cancel-guard

Conversation

@mittalpk

Copy link
Copy Markdown

Fixes #647.

Bug 1 — process.env.SHELL breaks Windows process spawning

runCommand (lib/process.mjs) and SpawnedCodexAppServerClient.initialize (lib/app-server.mjs) both consulted process.env.SHELL when deciding the shell: option on Windows. SHELL is a POSIX convention with no meaning for native Windows process creation — on a machine where it happens to be set to a POSIX shell path (e.g. Git Bash, which Claude Code's own Bash tool sets), spawn/spawnSync routes commands through that shell instead of cmd.exe, and MSYS's automatic POSIX-path conversion mangles Windows-style flags like taskkill's /PID. Fixed by always using true on win32, never consulting SHELL.

Bug 2 — handleCancel aborts before persisting job state on a partial kill failure

terminateProcessTree throws whenever taskkill exits non-zero for a reason other than "process not found" (matched via a narrow regex). On Windows, taskkill /T can fail to kill a subset of grandchild processes with a message that doesn't match that regex. handleCancel called it unguarded, so that throw aborted the whole cancel before the job's on-disk status was ever updated — leaving it permanently stuck at "running"/"finalizing" even though the turn interrupt (the part that actually matters) had already succeeded. Fixed by wrapping the call in try/catch and logging-but-continuing, matching terminateProcessTree's own "process already gone" best-effort semantics for this case too.

Testing

  • Added a process.test.mjs case proving terminateProcessTree still correctly throws for a genuine (non-"missing process") Windows taskkill failure — locking in the exact mechanism handleCancel's new guard depends on.
  • Ran the full suite (node --test tests/*.test.mjs): 92/92 pass, including all existing cancel end-to-end tests (confirming the happy path is unaffected).
  • npx tsc -p tsconfig.app-server.json (covers both changed lib/ files): clean.
  • Both bugs are Windows-only and not reproducible on macOS/Linux (confirmed in the issue itself), so the Windows-specific failure paths (mangled taskkill invocation, a genuine partial-tree-kill throw) can't be exercised end-to-end from this environment — verified via the injectable-dependency unit test above plus careful manual review, matching the reporter's own locally-patched-and-confirmed fix (they state they applied this same change to their local plugin cache and confirmed both symptoms resolved).

…e updating job state on a partial kill failure

Bug 1: runCommand and SpawnedCodexAppServerClient.initialize both
consulted process.env.SHELL when deciding the shell: option on
Windows. SHELL is a POSIX convention with no meaning for native
Windows process creation -- on a machine where it's set to a POSIX
shell path (e.g. Git Bash, which Claude Code's own Bash tool sets),
spawn/spawnSync routes commands through that shell instead of
cmd.exe, and MSYS's automatic POSIX-path conversion mangles
Windows-style flags like taskkill's /PID. Fixed by always using true
on win32, never consulting SHELL.

Bug 2: terminateProcessTree throws whenever taskkill exits non-zero
for a reason other than "process not found". handleCancel called it
unguarded, so a partial /T tree-kill failure on Windows aborted the
whole cancel before the job's on-disk status was ever updated,
leaving it stuck at running/finalizing forever even though the turn
interrupt had already succeeded. Fixed by wrapping the call in
try/catch and logging-but-continuing, matching terminateProcessTree's
own best-effort semantics for the already-gone case.

Fixes openai#647
@mittalpk
mittalpk requested a review from a team August 17, 2026 22:19

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c79ac1c133

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

terminateProcessTree(job.pid ?? Number.NaN);
try {
terminateProcessTree(job.pid ?? Number.NaN);
} catch (error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not report cancellation when termination fails

When the turn interrupt is unavailable or fails and terminateProcessTree also throws, this unconditional catch still reports success, marks the job cancelled, and clears its PID even though the worker may remain alive. A write-capable task can therefore keep modifying the workspace, and its later runTrackedJob completion can overwrite the cancelled state. Only suppress a failure proven to have stopped the root worker (or when the turn interrupt succeeded); otherwise preserve the PID and surface the cancellation failure.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: SHELL env var (Git Bash) breaks taskkill; handleCancel swallows terminateProcessTree exceptions, leaving jobs stuck in running/finalizing

2 participants