Windows Desktop: shell tool timeout does not release agent when a headless Chrome child survives
Summary
On Codex Desktop for Windows, a shell tool call that starts headless Chrome can remain blocked indefinitely after the declared tool timeout. The agent cannot send updates or run cleanup until the user manually terminates Chrome. Killing the browser process causes the pending tool call to return immediately.
This is not primarily a Chrome screenshot bug: an external child is allowed to hang, but Codex should still regain control at the requested timeout.
Environment
- Surface: Codex Desktop
- OS: Windows
- Shell: PowerShell
- Workload: local localhost page with a continuously rendered WebGL canvas
- No network dependency or privileged product mutation involved
Minimal reproduction shape
Run a shell tool with a bounded timeout_ms (for example 35 seconds). The PowerShell command launches chrome.exe --headless=new against a localhost WebGL page and waits for a screenshot artifact. The child does not produce the screenshot and remains alive.
A tracked repro emitted heartbeats at 5, 10, 15, and 20 seconds, then attempted exact-PID-tree cleanup. Cleanup received Access denied. Despite the shell tool declaring a 35-second timeout, the tool call stayed pending for roughly 78 minutes. The agent could not respond during that interval. When the user manually ended Chrome, the tool call returned immediately.
A previous direct chrome.exe --headless --screenshot attempt showed the same class of behavior and left its dedicated profile active for about two hours without producing the PNG.
Expected behavior
At timeout_ms, Codex should:
- stop waiting for the shell command;
- terminate the full process tree created by that call, ideally using a Windows Job Object;
- return a timeout result to the agent even if child cleanup fails;
- never require the user to open Task Manager to make the agent responsive again.
Actual behavior
- The child process tree survives the deadline.
- Sandbox cleanup can fail with
Access denied. - The pending shell tool call keeps the whole agent turn blocked.
- No commentary/heartbeat or alternative tool call is possible.
- Manual termination of Chrome is what releases the agent.
Additional Windows clue
In the same Codex shell environment, PowerShell Start-Process and even enumeration of Env: can fail with:
Item has already been added. Key in dictionary: 'Path' Key being added: 'PATH'
This suggests the host exposes case-variant environment keys which PowerShell copies into a case-insensitive dictionary. This is a separate spawn-path defect, but it complicates safe background-process/watchdog workarounds.
Suggested fix
- Put every shell tool invocation in a Windows Job Object owned by the tool runtime and close/terminate that job at timeout.
- Make timeout return control independently of in-sandbox cleanup success.
- Normalize case-insensitive environment keys before constructing a PowerShell child environment.
- Surface process-tree and cleanup status in the tool result.
Local mitigation
The affected workflow now avoids synchronous external Chrome CLI capture entirely and uses an already-connected browser/canvas export path. That prevents recurrence locally but does not address the Codex runtime issue.
3 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Additional sanitized reproduction from Codex Desktop on Windows (2026-07-24):
The problem is broader than headless Chrome. A minimal Node launcher invoked through the PowerShell shell tool also wedged the agent when it spawned a long-lived child with
{ detached: true, stdio: "ignore", windowsHide: true }, calledchild.unref(), printed a status line, and then calledprocess.exit(0). The launcher should have returned immediately, but the shell tool never released control. This reproduced twice, including with an explicit 5-second tool timeout. The Codex UI remained effectively frozen until the Windows desktop compositor was manually restarted; Task Manager showed the compositor using negligible CPU and System Idle around 97%, so this was not a CPU-saturated workload.A changed mechanism worked immediately: starting the same foreground supervisor from the already-persistent in-app-browser Node controller returned in 0.008 seconds, the HTTP service reached 200, and its heartbeat/PID tracking remained live. Starting it from the shell tool was the trigger.
This points to Windows shell-tool process-tree/job/handle ownership rather than application code or Chrome specifically. The runtime likely needs to either (a) place the whole spawned tree in a killable Job Object and terminate it at deadline, or (b) release the agent once the launcher exits even if a detached descendant survives. Timeouts must also restore agent steerability rather than waiting on descendant handles indefinitely.
Local mitigation: the project launcher now fails closed for
--detachon Windows and requires a persistent controller to own the foreground watchdog. No product-code or dependency change was needed.Corroborating the broader timeout/process-tree symptom on Codex Desktop for Windows, app package
26.721.4979.0_x64, command runner0.146.0-alpha.3.1.Local Next.js preview/dev-server commands that leave a persistent descendant can keep the Codex tool step shown as running for many minutes, including cases where the parent launcher was intended to detach or a command deadline had passed. The agent cannot provide progress or run cleanup while that call remains blocked; manually terminating the descendant releases it.
This has occurred with local preview workflows, not only Chrome. It began after the recent command-runner update and still reproduced after a full Codex restart. The separate duplicate
Path/PATHdefect also prevents the usual PowerShell redirected-Start-Processworkaround.A tool-runtime-owned Windows Job Object, with returning control at the declared timeout independent of cleanup success, would address the failure mode. No project paths, source code, environment values, or account data are included.