Windows: Bash/PowerShell tool subprocess trees appear to receive a broadcast Ctrl+C / console-control interrupt, killing unrelated sibling processes and the session itself
Summary
On Windows, during a long working session involving many Bash/PowerShell tool calls (including several run_in_background calls spawning nested subprocess trees via a project-local orchestration script), the entire process tree spawned by the session — including background child processes several layers deep — was repeatedly hit by what appears to be a Windows console-control interrupt (Ctrl+C / CTRL_C_EVENT-style), causing:
- The interactive
claudeCLI process itself to receive a Ctrl+C, triggering the hostcmd.exe's "Terminate batch job (Y/N)?" prompt and killing the session (requiringclaude --resume <session-id>). - A background subprocess spawned by a
Bashtool call withrun_in_background: true(running as a grandchild of the session, via a project script that usespywinpty/ConPTY) to independently receive the same class of interrupt at roughly the same time, evidenced by the child's own log:Got signal interrupt, shutting down, exit code0xc000013a(STATUS_CONTROL_C_EXIT), stderr^C.
The user did not press Ctrl+C (confirmed directly). Extensive investigation ruled out:
- The project's own code paths that call
taskkill/TerminateProcessfor cleanup (verified: these do not useGenerateConsoleCtrlEventand do not broadcast to a console). os.kill(pid, signal.SIGINT)on Windows in apywinptycleanup path (verified against CPython'sos_kill_impl: plainSIGINT, value 2, does not route throughGenerateConsoleCtrlEvent; onlyCTRL_C_EVENT/CTRL_BREAK_EVENTdo).- A recently-merged PR touching model config/self-care scripts in the same repo (verified: no functional connection to the dispatch/PTY path at all).
- A local pytest OOM-guard thread in the repo's own test suite (verified: it only calls
os._exit(1)on itself, never sends signals to other processes).
The pattern — multiple, independently-spawned descendants of the same Claude Code session dying near-simultaneously from what looks like a console-control broadcast, with no code in the affected repo sending one — points toward something in the Claude Code tool-execution/sandbox layer itself (Bash/PowerShell tool timeout enforcement, or a Windows Job Object / process-group-wide kill mechanism used to enforce a tool-call timeout or resource limit) as the most likely remaining explanation, though this could not be confirmed from inside the user's own repository since it's outside that codebase.
Environment
- OS: Windows 11
- Shell backing the
Bashtool: Git-Bash/MSYS (MINGW64_NT-10.0-26200) PowerShelltool: Windows PowerShell 5.1- Session involved many nested subprocess trees:
Bash/PowerShelltool calls -> project orchestration script (hub.py) ->pywinpty-based PTY child processes (an actual CLI tool, not Claude Code itself) -> that tool's own further child processes (e.g. its status-line renderer).
What we'd love clarity on
- Does the
Bash/PowerShelltool's timeout enforcement (either the per-calltimeoutparameter or an internal default) ever terminate a command via a Windows console-control broadcast (GenerateConsoleCtrlEvent) rather than a scopedTerminateProcess/job-object kill targeted at just that command's own process tree? - Is there a Windows Job Object (or similar container) that Bash/PowerShell tool-call subprocess trees run inside, and if a limit on that job is hit, does the resulting kill propagate as a Ctrl+C-style signal to everything in the job — including sibling/background tool calls that are otherwise unrelated to the one that hit the limit?
- Is there a supported way to opt a whole session (not just a single call via
dangerouslyDisableSandbox) out of whatever mechanism this is, to test the hypothesis without losing sandbox protections we do want?
Happy to provide the reproduction session and relevant log excerpts (the interrupted child's own log showing Got signal interrupt, shutting down / 0xc000013a / ^C) if useful — omitted here since they're local to the user's machine.