[Windows] Tool subprocess GenerateConsoleCtrlEvent can terminate the Codex App control plane

Open 💬 1 comment Opened Aug 13, 2026 by Wolfsblvt

What version of the Codex App are you using (From “About Codex” dialog)?

26.803.10989.0 x64

What subscription do you have?

Paid individual ChatGPT plan (exact tier is not relevant to this local process-isolation failure)

What platform is your computer?

Microsoft Windows NT 10.0.26200.0 x64

What issue are you seeing?

A PowerShell process launched through a Codex task can attach to another launched console and send a console-wide
CTRL_C_EVENT that immediately terminates or interrupts the Codex App/task control plane as well as the intended
target process chain.

This happened twice in one task. In each run:

  • a Codex-launched pwsh -> dotnet -> server chain was alive and serving HTTP;
  • the original PTY/session owner had been lost;
  • a new Codex tool subprocess called FreeConsole, AttachConsole(targetPwshPid), disabled its own Ctrl+C handling,

then called GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);

  • the Codex task/app control plane disappeared immediately, before the UI displayed a useful result; the persisted

transcript has no output event after the first call and an empty tool-output event 10.7 seconds after the second;

  • after reopening/resuming Codex, both the target process chain and listener were gone.

Observed control-call timestamps were 2026-08-13T21:45:26Z and 2026-08-13T21:50:40Z. Task/session id:
019ffc70-2514-7c13-8053-22489e43d5cf.

There was no visible exception dialog and no Win32 result displayed in the UI because the control plane exited during
the call.

What steps can reproduce the bug?

  1. In Codex App on Windows, launch a long-running foreground process chain through a task PTY, for example PowerShell

launching a local server.

  1. Lose or detach from the original PTY owner while the process chain remains alive.
  2. From a new Codex tool subprocess, run the equivalent of:
Add-Type @'
using System;
using System.Runtime.InteropServices;
public static class ConsoleControl {
    [DllImport("Kernel32", SetLastError = true)] public static extern bool FreeConsole();
    [DllImport("Kernel32", SetLastError = true)] public static extern bool AttachConsole(uint processId);
    [DllImport("Kernel32", SetLastError = true)] public static extern bool SetConsoleCtrlHandler(IntPtr handlerRoutine, bool add);
    [DllImport("Kernel32", SetLastError = true)] public static extern bool GenerateConsoleCtrlEvent(uint ctrlEvent, uint processGroupId);
}
'@

[ConsoleControl]::FreeConsole() | Out-Null
[ConsoleControl]::AttachConsole([uint32]$targetPwshPid) | Out-Null
[ConsoleControl]::SetConsoleCtrlHandler([IntPtr]::Zero, $true) | Out-Null
[ConsoleControl]::GenerateConsoleCtrlEvent(0, 0) | Out-Null
  1. Observe that Codex disappears/interruption occurs immediately rather than returning a tool result.

Do not reproduce this on a task carrying valuable uncommitted state. The operation is intentionally unsafe under
Windows console semantics.

Token-limit/context-window usage was not relevant. Both events occurred during ordinary local tool execution.

What is the expected behavior?

A console control event sent by an agent-launched tool subprocess should not terminate the Codex App or its task control
plane.

Ideally Codex app/control processes should not share a signal-reachable console boundary with task subprocesses. At
minimum, this unsafe subprocess action should fail or terminate only the active tool/task while the desktop app and
other tasks remain alive and recoverable.

Additional information

This is a high-confidence mechanism, not a proven Codex regression: no crash dump or returned control-call result was
available.

Microsoft documents that CTRL_C_EVENT cannot be limited to a specific nonzero process group and that process-group
0 sends the signal to all processes sharing the caller's console. That explains why treating it as target-process
control is unsafe:

Related issues #25272 and #25672 involve Codex/node-pty's own AttachConsole cleanup helper throwing while a terminal
closes. This report is the opposite direction: arbitrary code inside a Codex-launched tool attaches to a launched
console and sends a group-wide signal that appears able to take down the Codex control plane. They may share a Windows
console-isolation boundary, but the reproduction and failure are distinct.

Safe local workaround: retain the original Codex PTY/session and send Ctrl+C only through that owner. If ownership is
lost, do not call AttachConsole or synthesize a console control event; reconcile the process state read-only and
record shutdown proof as incomplete.

View original on GitHub ↗

1 Comment

shleder · 13 days ago

This is a useful recovery boundary to test independently because the persisted state may contain a tool call whose side effect is uncertain after the Codex control plane disappears.

Codex Rescue is deliberately conservative in that case: it can diagnose an unfinished/orphaned tool-call boundary, but it will not assume whether GenerateConsoleCtrlEvent actually executed and it will not replay the operation.

If the affected rollout is still present locally, would you be willing to run a doctor-only check with 0.1.0a3?

pipx install codex-rescue==0.1.0a3
codex-rescue sessions
codex-rescue doctor --latest

If the affected thread is not the latest one, please do not modify anything to make it latest; just say so. No salvage needed at this stage.

A HEALTHY result is useful too — it would mean Rescue did not recognize structural persisted-rollout damage and would keep the failure scoped to the upstream process/signal boundary.

Please share only sanitized status/findings. No raw rollout, command arguments, process IDs, local paths, repository contents, databases, credentials, or secrets.

https://github.com/shleder/codex-rescue