SkyComputerUseClient processes spawned for turn-ended events remain alive as PPID=1 orphans

Open 💬 6 comments Opened Jun 4, 2026 by kangminlee-maker
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

Codex Desktop appears to leave SkyComputerUseClient helper processes alive after turn-ended / agent-turn-complete events. On my machine these processes accumulate as PPID=1 orphans and are not tied to active Computer Use work.

This is different from simply having many active sessions. The process command lines show that these helpers were launched with a turn-ended event payload, including type: "agent-turn-complete", client: "codex_exec", cwd, thread-id, turn-id, input-messages, and last-assistant-message.

The accumulated helpers coincide with degraded Codex Desktop responsiveness, including laggy output streaming, elevated renderer/app-server CPU, and large local state/log databases.

Related but distinct from #24510, which covers large active history/list behavior. This issue is specifically about helper process lifecycle / reap behavior for turn-ended event handling.

Environment

  • App: Codex Desktop
  • Version observed in process list: 26.527.31326 / Electron helper version 149.0.7827.54
  • OS: macOS 26.4.1 25E253
  • Machine: Apple Silicon Mac
  • Local state paths involved:
  • ~/.codex/state_5.sqlite
  • ~/.codex/logs_2.sqlite
  • ~/.codex/computer-use/Codex Computer Use.app/.../SkyComputerUseClient

Observed process state

At the time of investigation:

SkyComputerUseClient total: 287
PPID=1 orphans:        285
current app-server child: 1
other transient shell/rg match: 1

All meaningful orphaned processes had command lines shaped like:

/Users/.../.codex/computer-use/Codex Computer Use.app/.../SkyComputerUseClient turn-ended {
  "type": "agent-turn-complete",
  "thread-id": "...",
  "turn-id": "...",
  "cwd": "/Users/kangmin/cowork/onto-mcp",
  "client": "codex_exec",
  "input-messages": [...],
  "last-assistant-message": "..."
}

They were not active children of the current Codex app-server. Most were inherited by launchd:

by_ppid [(1, 285), (48524, 1)]
by_type [('agent-turn-complete', 285)]
by_client [('codex_exec', 285)]

Distribution by workspace

The stale helpers mostly came from high-volume codex_exec review runs:

/Users/kangmin/cowork/onto-mcp                         255 processes
/Users/kangmin/Documents/excel-workbook-editing         19 processes
/Users/kangmin/Documents/FCeditor-pipeline-stabilization 8 processes
/Users/kangmin/cowork/onto-mcp-claude                    3 processes

Time clustering also suggests they were created during turn-completion bursts rather than a single app crash:

Mon Jun 1 11:00 /Users/kangmin/cowork/onto-mcp  85
Mon Jun 1 14:00 /Users/kangmin/cowork/onto-mcp  58
Mon Jun 1 13:00 /Users/kangmin/cowork/onto-mcp  52
Mon Jun 1 12:00 /Users/kangmin/cowork/onto-mcp  38
Tue Jun 2 09:00 /Users/kangmin/Documents/excel-workbook-editing 12

The current Codex Desktop app instance had started later than most of these helpers, so they were not freshly created by the current session:

Current Codex app start: Wed Jun 3 09:58:03 2026
Orphan helper starts:    Sat May 30, Mon Jun 1, Tue Jun 2

Impact observed

During the same investigation window, Codex Desktop became visibly sluggish:

  • assistant output streaming lagged in the UI
  • renderer CPU spiked
  • app-server CPU spiked
  • GPU/service process also showed repeated CPU bursts

Example process samples:

Renderer PID 48668:   up to ~140% CPU, RSS ~1.7-1.8 GB
app-server PID 48524: up to ~190% CPU, RSS ~790-820 MB
GPU/service PID 48495: up to ~48% CPU
Codex main PID 48458: 10-20% range during the lag window

Local DB sizes were also large at the time:

~/.codex/state_5.sqlite  1.3 GB
~/.codex/logs_2.sqlite   2.0 GB
~/.codex/logs_2.sqlite-wal 33 MB

Active thread state had grown again:

active threads:      10,379
active preview:      188.64 MB
active text total:   566.38 MB
archived threads:    6,970
archived preview:    181.41 MB

Largest active contributors:

/Users/kangmin/cowork/onto-mcp
  3,052 rows / 128.77 MB preview

/Users/kangmin/cowork/wireframe_editor_v2-segment-split
  5,280 rows / 28.35 MB preview

The large active history likely contributes to UI/server pressure, but the orphaned SkyComputerUseClient processes appear to be a separate lifecycle leak.

How I inspected it

Representative commands:

pgrep -fl 'SkyComputerUseClient' | wc -l

ps -axo ppid,comm \
  | rg 'SkyComputerUseClient' \
  | awk '{count[$1]++} END{for (p in count) print p,count[p]}' \
  | sort -k2 -nr

ps -ww -axo pid=,ppid=,lstart=,etime=,rss=,command= \
  | rg 'SkyComputerUseClient'

ps -ww -axo command= \
  | rg 'SkyComputerUseClient' \
  | rg -o 'turn-ended|agent-turn-complete|codex_exec|client":"[^"]+"|cwd":"[^"]+'

I also sampled the app-server and renderer during the lag window:

/usr/bin/sample 48668 3 -file /tmp/codex-renderer-48668.sample
/usr/bin/sample 48524 3 -file /tmp/codex-app-server-48524.sample

The renderer sample was mostly V8/JS/string/stream related. The app-server sample showed many tokio-runtime-worker and sqlx-sqlite-worker threads with SQLite I/O mixed in.

Expected behavior

A helper spawned for turn-ended / agent-turn-complete should not remain alive indefinitely after the event is handled.

At minimum, one of the following should happen:

  • the helper exits after successfully handling the event
  • the parent app-server reaps it
  • the helper has a lease/TTL and self-exits when stale
  • the helper exits when its parent app-server is gone
  • Codex Desktop startup reaps stale orphan helpers from prior runs

Actual behavior

The helpers remain alive for days with PPID=1, while still carrying their original turn-ended JSON payload in the command line.

This suggests one of:

  • the event handler process is never exiting
  • the parent loses ownership after spawning it
  • cleanup is only implemented on normal paths and misses turn/event completion paths
  • stale helper reaping is missing on app-server or app startup

Why this matters

This can silently accumulate across high-volume codex_exec workloads. In my case, hundreds of stale helpers were left behind, mostly from one workspace. Even if individual helpers are idle, the accumulation makes diagnosis hard and may contribute to degraded Desktop performance when combined with large local state/history.

The command-line payloads also contain large prompt and assistant-message data, so these orphan processes retain potentially large per-turn context in process arguments for much longer than needed.

Suggested fix direction

Treat SkyComputerUseClient event helpers as leased resources rather than fire-and-forget child processes:

  • include an owner app-server/session/turn id
  • ensure parent-death detection or explicit parent heartbeat
  • add a short TTL for turn-ended handlers
  • reap stale helper processes on Codex Desktop/app-server startup
  • avoid keeping full input-messages / last-assistant-message in long-lived process argv if the helper can outlive the event

The key invariant should be:

A turn-ended helper may outlive the turn briefly, but it should not outlive its owner indefinitely.

View original on GitHub ↗

6 Comments

github-actions[bot] contributor · 2 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #25744

Powered by Codex Action

kangminlee-maker · 2 months ago

Adding related issues I found after filing this:

  • #21008 reports Codex Desktop leaking orphaned MCP helper processes under launchd.
  • #12491 reports Codex.app GUI leaving codex exec / MCP child processes unreaped after task completion.

This issue seems to be in the same broad lifecycle/reaping family, but the leaked process class is different:

#21008 / #12491
  MCP / codex exec helper subtree not reaped

#26293
  SkyComputerUseClient processes spawned with `turn-ended` / `agent-turn-complete` payloads remain alive as PPID=1 orphans

The part that may be useful for debugging here is that these SkyComputerUseClient processes are not just generic Computer Use workers. Their argv still contains the original turn completion event payload:

SkyComputerUseClient turn-ended {
  "type": "agent-turn-complete",
  "client": "codex_exec",
  "cwd": "...",
  "thread-id": "...",
  "turn-id": "..."
}

So this may share the same root cleanup problem as the MCP helper leaks, but it appears to hit the turn-ended event path specifically.

ericlitman · 2 months ago

Adding an independent confirmation on a newer Codex Desktop build, with a smaller synthetic repro.

Environment

  • macOS: Darwin 25.5.0 arm64
  • Codex app: com.openai.codex 26.602.71036 build 3685
  • Bundled app CLI: codex-cli 0.137.0-alpha.4
  • Shell codex: codex-cli 0.135.0
  • Computer Use client: com.openai.sky.CUAService.cli 1.0 build 809
  • Notify command in user config before mitigation:
notify = ["$HOME/.codex/computer-use/Codex Computer Use.app/Contents/SharedSupport/SkyComputerUseClient.app/Contents/MacOS/SkyComputerUseClient", "turn-ended"]

Observed behavior

After a set of headless Codex review/council sessions completed, 21 stale matching processes were still alive. The user originally noticed these PIDs hours after the work had completed:

19369 92023 26160 53821 69252 12259 75017 94990 15143 46294 82912 39991 3817 29564 46473 50258 50838

A stricter process sweep found 21 total SkyComputerUseClient turn-ended ... clients at the time of cleanup. They were not active reviewer workers. They were orphaned under launchd, sleeping, and retained the completed turn payload in argv.

Representative observations:

  • ps -ww -o pid,ppid,pgid,stat,etime,time,lstart,command -p ... showed PPID=1, STAT=S, and about 0:00.01 CPU for each stale helper.
  • lsof -a -p <pid> showed stdio redirected to /dev/null, no child work, and the Computer Use client executable.
  • sample <pid> 3 showed the main thread blocked in CFRunLoopRun, with another thread blocked in xpc_pipe_receive / mach_msg2_trap.

Synthetic repro

Invoking the raw client directly with a minimal agent-turn-complete payload reproduced the hang:

CLIENT="$HOME/.codex/computer-use/Codex Computer Use.app/Contents/SharedSupport/SkyComputerUseClient.app/Contents/MacOS/SkyComputerUseClient"
PAYLOAD='{"type":"agent-turn-complete","thread-id":"probe-thread","turn-id":"probe-turn","cwd":"/tmp","client":"codex_exec","input-messages":["probe"],"last-assistant-message":"probe complete"}'
"$CLIENT" turn-ended "$PAYLOAD"

Expected: the command returns promptly after delivering or rejecting the event.

Actual: the foreground client process remained alive after the service had accepted the notification path. Unified logs for SkyComputerUseService during the synthetic probe showed the service sending the notification request and receiving HTTP status 202, but the client process still did not exit.

The stuck probe had the same blocked stack shape as the older orphaned helpers (CFRunLoopRun / XPC receive). It needed to be killed manually.

Impact / privacy note

The leaked processes keep the original turn-ended JSON payload in command-line arguments. For real sessions, that can include user prompt text and the last assistant message, so the leak is both a process-lifecycle issue and a data-retention-in-argv issue.

Local mitigation used

As a local workaround, I installed a small wrapper around the configured notify command. It does not parse the payload; it forwards all args to SkyComputerUseClient, waits a short bounded timeout, then sends SIGTERM / SIGKILL if the client does not exit. With a 3s timeout, repeated installed-path smokes kill the hung client and leave no lingering SkyComputerUseClient turn-ended rows afterward.

This workaround is intentionally at the Codex notify boundary. The upstream invariant should still be that a turn-ended helper exits promptly after handling the event, or that Codex bounds/reaps that helper itself.

Related public workaround PR for reference: https://github.com/ericlitman/crucible/pull/9

HubGroot · 2 months ago

Can confirm, with some additional data points from a clean reproduction today:

  • Reproduces on codex-cli 0.130.0 and 0.139.0 (updated specifically to test; latest as of 2026-06-10), macOS 15 (Darwin 25.5.0). Trigger: any codex exec turn with the legacy notify hook pointing at SkyComputerUseClient — one turn-ended orphan per turn, never exits (observed 30s+, oldest survivor ~18h; ~18 accumulated over one day of heavy exec use).
  • Reproduces even when SkyComputerUseService is running and freshly launched — so it does not appear to be "receiver not running".
  • Possibly related: neither com.openai.sky.CUAService nor com.openai.sky.CUAService.cli ever registers with macOS Notification Center (checked com.apple.ncprefs), and launching the .app directly spawns no GUI/permission prompt — so the dispatch path seems to never complete on machines where the Computer Use app was installed but never onboarded.
  • Each stub links AppKit/WebKit/ScreenCaptureKit/Network (≈10MB binary, tens of MB RSS), so accumulation is noticeable on long-lived machines.
  • Workaround verified: removing the notify = [...] line from ~/.codex/config.toml → zero new orphans. Independent of the client-side fix, it would be nice if codex reaped or applied a timeout to notify children (hooks/src/legacy_notify.rs) so a misbehaving notifier can't accumulate.
bchumerch · 2 months ago

Confirming this still reproduces on a newer build, and adding a data point where the leak escalated from "degraded responsiveness" to a full system OOM that took down WindowServer.

Environment

  • App: Codex Desktop 26.611.61753 (Electron helper 149.0.7827.115)
  • Computer Use service: SkyComputerUseService (persistent) + leaked SkyComputerUseClient turn-ended clients
  • OS: macOS 26.2 (25C56)
  • Machine: Apple Silicon, 24 GB RAM
  • Driver: several scheduled Desktop automations firing hourly. Every completed automation turn spawns one SkyComputerUseClient turn-ended {...} helper that reparents to launchd (PPID 1) and never exits.

Escalation: orphan accumulation → OOM → WindowServer watchdog kill

Over one afternoon the orphan count climbed steadily (sampled from the kernel JetsamEvent snapshots):

16:17  SkyComputerUseClient = 468
16:48  SkyComputerUseClient = 477
18:16  SkyComputerUseClient = 477   <-- OOM event

At 18:16 the machine hit a memory-pressure JetsamEvent (2,746 processes, ~82 GB resident demand on 24 GB physical; compressor ~7 GB, free pages near zero). Jetsam began culling processes, and ~34s later WindowServer was killed by the watchdog:

WindowServer-<ts>.ips  bug_type 409
termination: WATCHDOG "monitoring timed out for service"
  unresponsive work processor(s): WindowServer main thread
  40 seconds since last successful checkin

The user-visible result was a hard UI freeze followed by a forced graphics/login-session restart (looks like a crash/logout). The 477 orphans were each 0% CPU — pure retained memory with no useful work after their turn completed. After the session restart the count dropped to 2.

This looks like the same lifecycle bug as the original report (and overlaps #25744), just observed at the point where the retained memory is large enough to OOM a smaller-RAM machine rather than merely lag.

Workaround

Since the turn-ended helpers do nothing after the turn completes, reaping them is safe. A periodic launchd job clears them:

# kill only the per-turn notifier orphans; never the persistent SkyComputerUseService
pkill -f "SkyComputerUseClient turn-ended"

A proper fix would have the turn-ended / agent-turn-complete notifier exit (or be reaped by the app-server) instead of being inherited by launchd.

galligan · 22 days ago

Adding a note in support of the process-class distinction @kangminlee-maker drew above, since it turns out there is a third class worth separating.

On a current build (Codex Desktop 26.727.51351, CLI 0.146.0-alpha.9.2, CU runtime 26.727.1000550) I see SkyComputerUseClient accumulating with zero PPID=1 orphans — so this machine is not hitting the leak described here, despite having the turn-ended notifier configured:

notify = [
  ".../Codex Computer Use.app/Contents/SharedSupport/SkyComputerUseClient.app/Contents/MacOS/SkyComputerUseClient",
  "turn-ended",
]

Instead the accumulation is entirely SkyComputerUseClient event-stream mcp, retained as live children of the app-server, each holding ongoing periodic work (~1.0–1.33% of a core apiece, constant regardless of age). Details and measurements: https://github.com/openai/codex/issues/35659#issuecomment-5196161230

So the taxonomy looks like three distinct paths off the same binary:

#12491 / #21008
  MCP / codex exec helper subtree not reaped

#26293 / #29157
  SkyComputerUseClient `turn-ended` helpers -> PPID=1 orphans

#35659 (+ the SkyComputerUseClient portion of #25744)
  SkyComputerUseClient `event-stream mcp` -> live app-server children,
  each retaining a fixed-rate periodic work item

Noting it here in case it helps narrow whether these share a reaping root cause or are genuinely separate lifecycle bugs — the fact that one install can have the notifier configured and still show zero orphans while leaking heavily via event-stream suggests they may be independent.