Skip to content

macOS Computer Use leaks SkyComputerUseClient turn-ended helpers #29157

Open

Description

@llamallamaredpajama

Codex Computer Use leaks SkyComputerUseClient turn-ended helper processes on macOS

Summary

Codex Desktop / Computer Use on macOS leaves SkyComputerUseClient helper processes running after turns complete. The leaked helpers include orphaned processes whose parent is PID 1 (launchd) and nested chains of:

SkyComputerUseClient turn-ended --previous-notify <json>
  /bin/bash .../codex-notify.sh turn-ended -- SkyComputerUseClient turn-ended <json>
    SkyComputerUseClient turn-ended <json>

This accumulates over time and survives normal Codex turns/sessions. The command payload contains turn metadata, so examples here are redacted.

Environment

  • Host: macOS 27.0, build 26A5353q, arm64
  • Codex.app currently installed: 26.616.32156, build 4157
  • Originally observed version from the report: 26.608.12217
  • codex CLI: codex-cli 0.130.0
  • Computer Use bundle: ~/.codex/computer-use/Codex Computer Use.app
  • Computer Use bundle version: CFBundleShortVersionString=1.0, CFBundleVersion=829
  • Service executable: .../Codex Computer Use.app/Contents/MacOS/SkyComputerUseService
  • Client executable: .../SkyComputerUseClient.app/Contents/MacOS/SkyComputerUseClient

Reproduction

  1. Enable/use Codex Desktop Computer Use or any Codex path that installs the SkyComputerUseClient turn-ended notifier.
  2. Run several Codex turns or Computer Use actions.
  3. Check helper counts and parentage:
ps -Ao ppid,comm | awk '/SkyComputerUseClient/{print $1}' | sort | uniq -c | sort -rn
ps -Ao comm | grep -c SkyComputerUseClient
ps -Ao etime,pid,ppid,comm | grep SkyComputerUseClient | sort -r | head
ps -Ao pid,ppid,etime,comm | grep -E 'SkyComputerUseService|MacOS/Codex$'

Expected

After the relevant Codex turn/session ends, all per-turn SkyComputerUseClient helpers should exit. If the service exits, it should terminate and/or reap all children it spawned. A broken --previous-notify command should not leave the client process alive indefinitely.

Actual

Helpers remain alive for hours/days. On this machine, a previous run observed 1,422 simultaneous SkyComputerUseClient processes. In the current reproduction before cleanup:

  • ps -Ao comm | grep -c SkyComputerUseClient returned 22.
  • ps -Ao ppid,comm | awk '/SkyComputerUseClient/{print $1}' ... showed 7 clients parented by PID 1.
  • The oldest current helpers were over 02:13:00 old.
  • The service process was alive as a child of Codex:
/Applications/Codex.app/Contents/MacOS/Codex
  ~/.codex/computer-use/Codex Computer Use.app/Contents/MacOS/SkyComputerUseService

Unified logs showed turn-end events with no active lock-screen threads while helper processes remained:

SkyComputerUseService: Received lock-screen turn end ... activeThreadCount=0 autoUnlockedThreadCount=0

Evidence

  • Info.plist identifies the service as com.openai.sky.CUAService and the nested client as com.openai.sky.CUAService.cli.
  • Entitlements show both service and client share the 2DC432GLL2.com.openai.sky.CUAService app group.
  • The bundled Computer Use plugin launches the MCP server via:
{
  "command": "./Codex Computer Use.app/Contents/SharedSupport/SkyComputerUseClient.app/Contents/MacOS/SkyComputerUseClient",
  "args": ["mcp"]
}
  • The leaked process command lines were turn-ended notifier invocations, not active UI interaction commands.
  • The local ~/.codex/config.toml had been rewritten to:
notify = [ "SkyComputerUseClient", "turn-ended", "--previous-notify", "[...codex-notify.sh..., ...SkyComputerUseClient..., turn-ended]" ]

This creates a self-wrapped notifier chain when an existing wrapper already preserves the original SkyComputerUseClient turn-ended notifier.

Probable root cause

There are two lifecycle gaps:

  1. SkyComputerUseClient turn-ended can block indefinitely after spawning/invoking the previous notifier chain. Its parent exits or detaches, leaving the helper reparented to launchd.
  2. The Computer Use service / installer wraps the existing Codex notify command without detecting that the previous notifier chain already includes SkyComputerUseClient turn-ended, creating a nested self-wrapper. The service also does not reap stale turn-ended helpers after turn completion.

Local mitigation installed

I installed a local, age-filtered mitigation:

  • Reset ~/.codex/config.toml to call the local notify wrapper first, then the original SkyComputerUseClient turn-ended.
  • Hardened ~/.config/dev-knowledge/hooks/codex-notify.sh:
    • strips recursive SkyComputerUseClient --previous-notify chains,
    • runs the original notifier with a bounded timeout (DK_CODEX_NOTIFY_ORIGINAL_TIMEOUT_SECONDS, default 20),
    • logs timeout/recursion warnings explicitly.
  • Added /Users/jeremy/.local/bin/codex-computer-use-reaper.
    • Default threshold: 3600 seconds.
    • Matches only the exact SkyComputerUseClient executable path.
    • Redacts turn JSON in logs.
  • Added and loaded LaunchAgent:
    • /Users/jeremy/Library/LaunchAgents/com.jeremy.codex-computer-use-reaper.plist
    • Runs at load and every 300 seconds.

Verification after mitigation:

  • Dry-run found 16 helpers, 14 older than 3600 seconds, 2 skipped as fresh.
  • Real run reaped 14 stale helpers and left the 2 fresh helpers untouched.
  • launchctl print gui/501/com.jeremy.codex-computer-use-reaper succeeds and reports interval 300 seconds.
  • Wrapper timeout test exits 124 when a notifier exceeds the configured timeout; pass-through notifier test exits 0.

Requested upstream fix

  • Make SkyComputerUseClient turn-ended always exit after notifying the service and/or invoking --previous-notify, with a bounded timeout for the previous notifier.
  • Detect and reject recursive --previous-notify chains that already include SkyComputerUseClient turn-ended.
  • Ensure SkyComputerUseService tracks all spawned helper PIDs and tears them down on turn end/service exit.
  • Add a stale-helper guard in the service: cap concurrent helpers and terminate old turn-ended helpers safely.

Activity

  1. github-actions commented on Jun 20, 2026

    @github-actions
    Contributor

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

    Powered by Codex Action

  2. PeterShanxin commented on Aug 15, 2026

    @PeterShanxin

    Same self-nesting notify chain reproduces on Windows (ARM64), not just macOS - with the Windows Computer Use helper codex-computer-use-arm64.exe. Adding the platform data point plus a measured growth rate and a workaround that stops it.

    Environment

    OS Windows 11, ARM64
    Package OpenAI.Codex_26.810.6296.0_arm64__2p2nqsd0c76g0
    Version resource Codex 151.0.7922.137
    Helper ...\AppData\Local\OpenAI\Codex\runtimes\cua_node\<hash>\bin\node_modules\@oai\sky\bin\windows\codex-computer-use-arm64.exe

    What ~/.codex/config.toml looked like

    Line 11 was a single notify entry wrapped inside itself 4 times, each level re-escaping the previous one as --previous-notify:

    Metric Value
    Line length 1,966 characters
    --previous-notify levels 4
    Longest consecutive backslash run 32

    Structure (elided):

    notify = [ "...\codex-computer-use-arm64.exe", "turn-ended", "--previous-notify",
      "[\"...codex-computer-use-arm64.exe\",\"turn-ended\",\"--previous-notify\",
        \"[\\\"...codex-computer-use-arm64.exe\\\",\\\"turn-ended\\\",\\\"--previous-notify\\\",
          \\\"[\\\\\\\"...codex-computer-use-arm64.exe\\\\\\\",\\\\\\\"turn-ended\\\\\\\"]\\\"]\"]" ]

    Note the two levels reference different runtime hashes (45b665c9b82ad082 at the outer levels, be1d88aa93cd4c41 at the innermost), so the chain has survived at least one runtime upgrade - the wrapper keeps pointing at helper binaries from older runtime directories.

    It grows on app start, and that is measurable

    config.toml mtime tracked the app launch exactly. Timeline from one session:

    • 09:08:50 - app relaunched -> notify gained another nesting level
    • Escape depth doubles per level, so length growth is roughly geometric

    Workaround that actually stops the re-injection

    Deleting the notify line alone is not enough - it comes back on the next launch. What stopped it was disabling the feature as well:

    [features]
    computer_use = false

    After that change, the app rewrote config.toml twice more (mtimes 09:54:06 and 10:00:28, confirming it does rewrite the file at startup) and notify was not re-injected either time. The computer_use = false line was preserved across both rewrites.

    Related config left behind by Computer Use on Windows, for reference:

    SKY_CUA_NATIVE_PIPE = "1"
    SKY_CUA_NATIVE_PIPE_DIRECTORY = '\\.\pipe\codex-computer-use-<guid>'

    Suggested fixes

    1. When registering the turn-ended notifier, detect that the current notify value already points at codex-computer-use* and replace it rather than wrapping it. The existing wrapper already preserves the original command, so re-wrapping is always redundant.
    2. Cap the chain depth and refuse to write a notify value beyond a sane length.
    3. On startup, collapse any already-nested chain down to its innermost original command - this repairs existing installs, which will otherwise keep growing forever.
    4. Drop wrapper entries whose helper path points into a runtime directory that no longer exists.

    Note

    I found this while investigating an unrelated (and much larger) performance problem on the same machine - sustained ~1 GB/s of logical file reads from the Electron main process. I want to be explicit that I have not established any causal link between the notify nesting and that read storm; at 1,966 characters this line is far too small to account for it. Reporting the nesting purely as its own bug.

  3. waml commented on Aug 15, 2026

    @waml

    this is slowing down my mac to a crawl impossible to use desktop please reset tibo as compensation lmao

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appbugSomething isn't workingcomputer-useperformance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions