Codex Desktop leaves orphaned zsh shell-snapshot processes burning ~100% CPU
Summary
Codex Desktop intermittently leaves orphaned shell snapshot subprocesses running under launchd. Each process is /bin/zsh -lc ... running Codex's shell snapshot capture command, and each burns roughly one full CPU core indefinitely until manually killed.
This appears distinct from existing CLI orphan/native-process issues because the runaway process is not the codex native binary or Node wrapper. It is the shell snapshot subprocess itself.
Environment
- Codex Desktop:
26.527.31326, build3390 - Codex CLI/runtime version recorded locally:
0.133.0 - macOS:
26.5(25F71) - Architecture:
arm64 - Shell:
/bin/zsh - App bundle:
/Applications/Codex.app
Observed behavior
Multiple orphaned /bin/zsh processes appear with PPID 1, each consuming about 97-100% CPU.
Observed process command shape:
/bin/zsh -lc if [[ -n "$ZDOTDIR" ]]; then
rc="$ZDOTDIR/.zshrc"
else
rc="$HOME/.zshrc"
fi
[[ -r "$rc" ]] && . "$rc"
print '# Snapshot file'
print '# Unset all aliases to avoid conflicts with functions'
print 'unalias -a 2>/dev/null || true'
print '# Functions'
functions
...
setopt
alias -L
export -p
Concrete examples from this machine:
- 2026-05-30: four orphaned snapshot processes, each around
98-100%CPU for about45-46minutes, total roughly 400% CPU. Killing only those PIDs immediately improved CPU idle from about36%to about69%. - 2026-05-31: two more orphaned snapshot processes:
- PID
77383, about97%CPU, running about22minutes - PID
4502, about97%CPU, running about8minutes - After killing those exact PIDs,
pgrep -lf "print '# Snapshot file'"returned nothing and load average began dropping.
Diagnostics performed
The same shell snapshot pattern completes quickly when run directly with a hard timeout:
time perl -e 'alarm shift; exec @ARGV' 10 /bin/zsh -lc '[[ -r "$HOME/.zshrc" ]] && . "$HOME/.zshrc"; functions >/dev/null; setopt >/dev/null; alias -L >/dev/null; export -p >/dev/null; print ok'
Result: completed successfully in about 1s.
Snapshot files are being written under:
~/.codex/shell_snapshots/
Recent files included:
019e7a0e-5d2e-77a0-b27b-ecb6a3c1739f.1780213284925867000.sh
019e7cfc-2f3f-7ce3-aef9-e0aab975fba9.1780213362784461000.sh
This suggests the capture often reaches the output stage, but the subprocess may not exit or Codex may lose cleanup/ownership.
I also saw stale process-manager entries in:
~/.codex/process_manager/chat_processes.json
And archived Codex session output showed similar previously orphaned snapshot commands, including long-running examples.
Expected behavior
Codex Desktop should ensure shell snapshot subprocesses terminate promptly, and should kill/timeout them if snapshot capture hangs or if the parent session/thread is interrupted or restarted.
Actual behavior
Shell snapshot subprocesses can become orphaned under launchd and continue spinning indefinitely at about one CPU core each.
Impact
- Machine runs hot.
- Multiple orphaned snapshot processes accumulate across sessions.
- CPU usage can reach several full cores until manually cleaned up.
Related issues
These may be adjacent but appear to cover different process types/failure modes:
- openai/codex#13928: Codex native binary persists after session exit and consumes CPU
- openai/codex#14962: CLI orphan process pairs after terminal close/SIGHUP
- [#10883](<https://github.com/openai/codex/issues/10883>): shell snapshot compatibility with runtime shell
Suggested next diagnostic
If this recurs, I can collect a sample before killing the process:
sample <pid> 5
That should identify where zsh is spinning.
3 Comments
Confirmed this is a thing on my machine. The fan was on for no reason, I checked and there were 5-7 zsh processes that had to be killed manually.
Same issue from the CLI
I hit the same problem through Codex CLI on macOS, not the desktop app. Here is what I found:
zshprocesses passed my full identity checks. One earlier pilot was verified separately from its PPID, TTY, children, command shape, and snapshot markers.One process had been using most of a core for more than an hour:
What helped
SIGTERMto that exact process.matched=11 skipped=0 terminated=11 survived=0. The pilot had already been terminated separately.Interactive NVM still works, and I have not seen new matches since making this change. That does not prove NVM caused the issue, and it does not fix Codex cleanup.
What may be happening
shell_snapshotpath, and the version on currentmain, still usesetsid(),kill_on_drop(true), and a timeout without explicitly cleaning the process group.This fits the failure, although a descendant must exist when the timeout occurs.
What I think Codex should change
I have a cleanup tool and isolated Tokio reproducer, and can share either to validate a fix
Additional confirmation with process-group and stack evidence
I can confirm this is still reproducible on a newer Codex Desktop build, and the evidence strongly supports the process-group cleanup explanation in the previous comment.
Environment
26.730.61639, build62340.147.026.5.2(25F84), arm640.39.0/Applications/ChatGPT.app, bundle idcom.openai.codexPID/PGID timeline
Two incidents were correlated from Codex's SQLite logs, macOS unified logs,
ps, andlsof:Afterward:
The original group leaders no longer existed, but the surviving processes retained their dead leaders' PGIDs. Both had no TTY, stdin was
/dev/null, and stdout/stderr were pipes whose reader ends no longer existed. The Codex backend process that launched the snapshots was still running, so reparenting did not require a full app exit or restart.This looks like: the timeout kills the direct shell/group leader, a zsh descendant created during shell initialization survives in the same process group, and the kernel reparents that descendant to PID 1.
CPU stack
A 3-second native sample of one survivor collected 2,452 stack samples:
execwhilegetoutput), and a shellwhileloopsigprocmaskstatlsofshowed the process still had both.zshrcand.nvm/nvm.shopenwrite,poll,kevent,functions,alias, orexportThe hottest path is therefore a zsh loop during NVM initialization, before the snapshot reaches the functions/aliases/exports output phase. The strongest candidates are NVM's upward path-search functions such as
nvm_find_project_dirornvm_find_up, but the native stack does not expose the exact shell function name, so that part remains unconfirmed.The closed output pipes appear to be a consequence of the timeout, not the cause of the CPU spin.
Recurrence and output-size evidence
At the time of inspection:
.zshrccompleted in about 5 seconds, and isolatedfunctions,setopt, aliases, and exports checks all exited successfullyThis argues against machine performance, snapshot output size, or an unconditional
.zshrcinfinite loop as the primary cause.Interpretation
The NVM loop is an environment-dependent trigger for the 10-second timeout, but the persistent orphan/high-CPU behavior is a Codex lifecycle bug: timeout/cancellation cleans up the direct child but not all descendants in the snapshot process group.
A regression test that has the snapshot shell fork a busy or sleeping descendant, then verifies that both the leader and descendant are gone after timeout/cancellation, should cover this failure mode.