Windows: Bash/PowerShell tool subprocess trees appear to receive a broadcast Ctrl+C / console-control interrupt, killing unrelated sibling processes and the session itself

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 5, 2026

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:

  1. The interactive claude CLI process itself to receive a Ctrl+C, triggering the host cmd.exe's "Terminate batch job (Y/N)?" prompt and killing the session (requiring claude --resume <session-id>).
  2. A background subprocess spawned by a Bash tool call with run_in_background: true (running as a grandchild of the session, via a project script that uses pywinpty/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 code 0xc000013a (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/TerminateProcess for cleanup (verified: these do not use GenerateConsoleCtrlEvent and do not broadcast to a console).
  • os.kill(pid, signal.SIGINT) on Windows in a pywinpty cleanup path (verified against CPython's os_kill_impl: plain SIGINT, value 2, does not route through GenerateConsoleCtrlEvent; only CTRL_C_EVENT/CTRL_BREAK_EVENT do).
  • 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 Bash tool: Git-Bash/MSYS (MINGW64_NT-10.0-26200)
  • PowerShell tool: Windows PowerShell 5.1
  • Session involved many nested subprocess trees: Bash/PowerShell tool 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

  1. Does the Bash/PowerShell tool's timeout enforcement (either the per-call timeout parameter or an internal default) ever terminate a command via a Windows console-control broadcast (GenerateConsoleCtrlEvent) rather than a scoped TerminateProcess/job-object kill targeted at just that command's own process tree?
  2. 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?
  3. 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.

View original on GitHub ↗