[macOS] ChatGPT 26.810.52044 respawns SkyComputerUseService and crashes despite Computer Use disabled

Open 💬 7 comments Opened Aug 15, 2026 by bangyuwen
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

The ChatGPT macOS desktop app repeatedly respawns SkyComputerUseService and then crashes, even when Computer Use and Remote Connections are disabled in the user configuration.

This is a new reproduction on ChatGPT desktop 26.810.52044 (build 6662) and appears related to the existing Computer Use / Node-V8 crash and helper-process reports.

Environment

  • ChatGPT desktop: 26.810.52044 (build 6662)
  • macOS: 26.5.2
  • Bundle ID: com.openai.codex
  • Computer Use runtime: 26.812.1000717
  • Apple Silicon Mac

Reproduction

  1. Fully quit ChatGPT and Computer Use helper processes.
  2. Set the effective app-server configuration to:
  • computer_use = false
  • remote_connections = false
  • no Computer Use notify hook
  1. Disable persisted remote-control state (remoteControl/disable returned status=disabled) and clear the selected remote host / auto-connect map.
  2. Launch ChatGPT desktop without invoking Computer Use.
  3. Observe the process tree.

Observed on a clean launch:

  • ~8 seconds: 10 SkyComputerUseService processes
  • ~23 seconds: 149 SkyComputerUseService processes
  • ChatGPT then becomes unstable and crashes.

On launch, the desktop frontend also rewrites the Computer Use notify hook and re-selects a persisted remote-control:env_* host, despite the disabled effective app-server state.

Crash evidence

The latest ChatGPT crash report has:

  • EXC_CRASH
  • SIGABRT / Abort trap 6
  • V8Worker
  • computer-use
  • node::OOMErrorHandler

The crash report was generated by ChatGPT desktop 26.810.52044 (build 6662). Full .ips data is available privately if needed; it is not attached here because it contains local paths and identifiers.

Expected behavior

When Computer Use and Remote Connections are disabled, ChatGPT should not launch or respawn SkyComputerUseService, and the desktop app should remain usable.

Actual behavior

The frontend calls the remote-control enable path and respawns many native Computer Use helper processes. Disabling the visible configuration, calling the app-server disable method, clearing persisted remote-host selection, and killing the helpers do not prevent the next launch from recreating them.

Impact

The desktop UI becomes unusable, helper processes consume substantial memory and process slots, and the main ChatGPT process exits. The current safe workaround is to keep the desktop app closed and use the web client.

Related reports

  • #26079 - repeated Node/V8 OOM crashes in the desktop app
  • #26458 - repeated Computer Use crashes and node::OOMErrorHandler
  • #28244 - macOS helper/zombie process explosion at startup
  • #25269 - multiple SkyComputerUseService instances respawn after being killed

Please route this to the ChatGPT desktop / Computer Use / remote-control owners. The key regression is that the frontend appears to re-enable and respawn Computer Use despite the disabled effective configuration.

View original on GitHub ↗

7 Comments

github-actions[bot] contributor · 12 days ago

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

  • #38760
  • #38697
  • #38744
  • #38720

Powered by Codex Action

FourCLabs · 12 days ago

I independently reproduced what appears to be the same Computer Use spawn storm on macOS 26.6.1. I added a detailed A/B isolation test and process dump in #38771; blocking ~/.codex/computer-use stopped SkyComputerUseService spawning, and restoring it immediately reproduced hundreds of helpers, 300%+ mds_stores CPU, lag, and a Codex crash.

PMWang · 10 days ago

I hit this exact failure on the same build and think I have the root cause, plus a workaround that keeps Computer Use fully enabled.

Trigger: the Codex home / service path is spelled through a symlink.

In my setup the real Codex home lives on a separate APFS volume and ~/.codex is a symlink to it. That symlink spelling is what ends up in SKY_CUA_SERVICE_PATH, and it is what drives the storm. Canonicalizing every runtime path makes the storm disappear completely — same app, same version, same volume, Computer Use on.

Why disabling Computer Use in config doesn't help

It doesn't, and I can confirm your finding. [plugins."computer-use@openai-bundled"] enabled = false and [mcp_servers.computer-use] enabled = false do not stop the managed service, and the app rewrites the notify hook back into config.toml on every launch. A launch with a completely empty CODEX_HOME (no config.toml at all) still produced 220 services in 65 s — so the spawn is not config-driven.

Where the comparison goes wrong

From app.asar: when SKY_CUA_SERVICE_PATH is set it is returned verbatim, un-normalized, and then used as expectedExecutablePath in

computerUseServiceProcessMatchesExecutablePath(pid, expectedExecutablePath)

which the CUA manager uses as isExpectedServiceProcess. The expected path is built as serviceAppPath/Contents/MacOS/SkyComputerUseService.

The AppleEvent is sent from Contents/Resources/native/sky.node:

-[RemoteHostedPIPContentService
  sendBootstrapToServiceWithProcessIdentifier:rendezvousPort:attempt:]
  -> AESendMessage      // returns -600, procNotFound

sky.node imports both posix_spawn and proc_pidpath, which is consistent with "spawn, read the PID's executable path, compare it to the expected path". proc_pidpath returns the resolved path (/Volumes/...) while expectedExecutablePath keeps the symlink spelling (/Users/<user>/.codex/...), so the two strings can never be equal. The identity/readiness check therefore fails every time, the PID is discarded, and a new service is spawned — with no readiness wait, no backoff, no single-flight, and no cap.

So in this reproduction the -600 and the stdio_connection_closed EOF are downstream of the identity mismatch, not the initial cause.

This is not TCC or LaunchServices: the AppleEvent targets a PID directly rather than resolving a bundle URL, tccd logged zero denials for com.openai.sky.CUAService across 20 minutes of storming, and sky.node does not import SecCode*, realpath, or readlink.

There is a second un-normalized use as well: the "canonical refresh" step dittos the source service into ${CODEX_HOME}/computer-use/Codex Computer Use.app without canonicalizing first.

Controls

Six configurations, each monitored 6 minutes (liveness, ps -M thread count, pgrep SkyComputerUseService count, and whether the main thread is still in nextEventMatchingMask):

| App location | CODEX_HOME | Service path | Result |
|---|---|---|---|
| External vol | symlink | via symlink | crash 158 s, 434 services |
| Internal disk | symlink | via symlink | crash 126 s, 385 |
| External vol | symlink | symlink → internal | crash 158 s, 417 |
| Internal disk | symlink | real | no crash, but 61 services leaked at startup, then flat |
| Internal disk | real | real | 1 service, stable |
| External vol | real | real | 1 service, stable |

What the matrix shows:

  • App/volume location is not the determining factor — an internal-disk copy crashes just as fast when the paths are symlinked.
  • Canonicalizing only the service path removes the infinite loop but still leaks a finite number of services at startup, which implies at least one additional path-derived state key.
  • Canonicalizing every runtime path yields exactly 1 service.

Also matching your report: 183 of 314 threads named computer-use, all blocked in AESendMessage; Dispatch Thread Soft Limit Reached: 64; faulting thread a V8Worker in node::OOMErrorHandler; Abort trap: 6. Separately, the main/UI thread blocks in node::SyncProcessRunner::Spawn (child_process.spawnSync), which is why the window freezes before the crash.

One secondary effect worth noting: TRACE logging into logs_2.sqlite reached 2.47 GB / 81.4 M rows during the storms, after which sqlx::pool::acquire started warning time to acquire exceeded slow threshold acquired_after_secs=2.34. That amplifies the failure but is not the cause.

Workaround (Computer Use stays fully enabled)

  1. Point CODEX_HOME at the real path, and make it visible to GUI launches:

``
launchctl setenv CODEX_HOME /real/path/to/codex-home
`
Persist it with a
RunAtLoad` LaunchAgent — a plain shell export is not enough, because double-click launches only inherit the variable this way.

  1. Rewrite every symlinked path inside config.toml to its real path (SKY_CUA_SERVICE_PATH, CODEX_HOME, CODEX_CLI_PATH, NODE_REPL_* trusted paths).

Both steps are needed; doing only one leaves the 61-service startup leak from the matrix above. After this the app preserves the real paths when it rewrites config.toml itself. Result here: 1 service, no crash, 27 h continuous uptime with Computer Use enabled.

Suggested fix

  1. Canonicalize once at the process boundary, or compare stable file/code identity instead of path strings.
  2. Independently of (1), the spawn path needs a single in-flight spawn, an explicit readiness wait before the first AppleEvent, bounded exponential backoff, a process cap, and a circuit breaker. A failing identity check should not be able to create hundreds of processes.
  3. A user-facing switch that actually stops the managed service would have made this survivable — today neither config flag does.

Environment: ChatGPT macOS 26.810.52044 (build 6662), Computer Use runtime 26.812.1000717, macOS 26.5.2 (25F84), Mac mini M4, Codex home on an external APFS volume reached via a symlink at ~/.codex.

Possibly the same underlying cause as #38697.

FourCLabs · 10 days ago

Thanks @PMWang — this is very useful. I’m one of the users who reproduced the SkyComputerUseService spawn storm above.

One additional A/B result since my earlier comment: I rolled the ChatGPT/Codex app itself back from 26.810.52044 / build 6662 to 26.803.61601 / build 6396 using an Aug. 7 Time Machine copy, while keeping macOS 26.6.1, my existing Codex data/environment, and Computer Use enabled.

So far, build 6396 is stable and the SkyComputerUseService spawn storm has not reproduced. Restoring/running build 6662 had reproduced the storm immediately.

I have not intentionally configured ~/.codex as a symlink or moved CODEX_HOME to another volume, so I’ll check whether any relevant paths in my environment resolve through symlinks. If none do, my case may indicate there is another path-normalization/identity mismatch that can trigger the same underlying spawn loop.

I’ll report back once I verify the paths.

brian-wijaya · 10 days ago

Independent reproduction on the same desktop and Computer Use builds.

Environment:

  • ChatGPT desktop 26.810.52044 (build 6662)
  • Computer Use 26.812.1000717 (build 1000717)
  • macOS 26.4.1 (25E253), Apple Silicon

Observed:

  • 8 ChatGPT crashes in about 20 minutes, mostly SIGABRT in node::OOMErrorHandler on a V8Worker
  • 343 SkyComputerUseService children under one ChatGPT process
  • the helper count stayed exactly 343 across multiple samples
  • sending TERM to all 343 verified helper PIDs caused the originals to exit, but ChatGPT immediately created exactly 343 replacements
  • plugin and MCP config flags were set false; the desktop app rewrote the Computer Use notify hook anyway
  • moving ~/.codex/computer-use/Codex Computer Use.app aside stopped further growth; no later ChatGPT crash report appeared
  • the helper itself had four earlier SIGABRT crashes in __assert_rtn / IOSurface initialization

This matches the path-identity/restart-loop diagnosis above and reproduces on macOS 26.4.1 as well. The one-for-one replacement strongly suggests the running desktop process retains one restart controller per failed helper, so killing child processes cannot recover it; the app must be restarted after blocking the bundle.

I can provide the raw .ips reports privately after redaction if useful.

iuxxoo · 9 days ago

Independent confirmation of the symlink/path-identity workaround described above.

In an affected setup, ~/.codex resolved through a symlink. The helper's expected path retained the symlink spelling while macOS reported the running executable using its canonical path. Making all related paths use one canonical spelling stopped the SkyComputerUseService respawn loop while keeping Computer Use enabled.

The working sequence was:

  1. Fully quit the desktop app.
  2. Resolve the real Codex home path, e.g. realpath ~/.codex.
  3. Set CODEX_HOME to that real path for GUI launches:

``sh
launchctl setenv CODEX_HOME /real/path/to/codex-home
``

  1. Rewrite Codex/Computer Use paths in the active config.toml to the same real path, including relevant NODE_REPL_* trusted paths/services.
  2. Relaunch the app.

Setting only the environment variable was not sufficient while active config paths still used the symlink spelling.

After canonicalizing both the GUI environment and config, the app held exactly one Computer Use helper and remained stable through real read-only Computer Use calls. This is a useful workaround for the symlink-triggered case, not a complete product fix. The spawn path still needs canonical path comparison plus a single-flight guard, bounded backoff/process cap, helper cleanup, and a circuit breaker so any identity/handshake failure cannot create an unbounded process storm.

PMWang · 8 days ago

Thanks @iuxxoo for the independent confirmation — in particular for reproducing that setting only the environment variable is not enough while the active config.toml still carries the symlink spelling. That matches the fourth row of my matrix (service path canonical, CODEX_HOME still symlinked → no crash, but a finite startup leak).

@FourCLabs — a symlink here does not have to be intentional, so it is worth checking mechanically rather than from memory. macOS ships several by default (/tmp, /var, /etc/private/*), and a relocated or migrated home directory, a home on a second volume, or launching the app from ~/Applications instead of /Applications can all introduce one without anybody configuring it.

This read-only check prints any path that resolves to something other than itself:

CH="${CODEX_HOME:-$HOME/.codex}"
echo "CODEX_HOME (effective): $CH"
{ printf '%s\n' "$HOME/.codex" "$CH" /Applications/ChatGPT.app "$HOME/Applications/ChatGPT.app"
  [ -f "$CH/config.toml" ] && grep -oE '/[^"]+' "$CH/config.toml" | sed 's/["'"'"',].*//'
} | sort -u | while IFS= read -r p; do
  [ -e "$p" ] || continue
  r=$(python3 -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' "$p" 2>/dev/null) || continue
  [ "$p" = "$r" ] || printf 'MISMATCH  %s\n     ->   %s\n' "$p" "$r"
done

Reading the output: any MISMATCH line for a path the app uses at runtime is a candidate trigger. One exception — after applying the workaround, ~/.codex itself will still report a mismatch, and that is fine, because CODEX_HOME and the config paths are the canonical ones and ~/.codex is then only a human-facing entry point. That is exactly the state on my machine right now, stable.

If your check comes back with no runtime mismatch, that is a useful result rather than a contradiction, and I would rather have that on the record than have my symlink case treated as the whole story. @brian-wijaya's report already points the same way:

  • Their helpers had their own SIGABRT in __assert_rtn / IOSurface initialization. A helper that dies during startup for a reason entirely unrelated to paths will fail the same readiness/identity check, and therefore fall into the same retry loop. That is a second, symlink-independent entry point into the same defect.
  • Their "kill all 343, get exactly 343 back" observation is the sharpest evidence in this thread for what is structurally missing. A one-for-one replacement means the desktop process retains one restart controller per failed helper, so the count is not a coincidence and killing children cannot recover it. That is precisely the absent single-flight guard: there should be one in-flight spawn attempt, not one per previous failure.

So I would summarise the current state of the thread as: the unbounded, uncapped, un-backed-off restart path is the product defect, and a path-identity mismatch is one reliable way to trigger it — reliable enough to be a clean repro, but not the only way in. The canonicalization workaround helps the symlink-triggered subset (mine, @iuxxoo's) and will not help someone whose helper is crashing on IOSurface.

@FourCLabs's rollback result is consistent with that framing too: 26.803.61601 (6396) stable, 26.810.52044 (6662) storming on the same machine, same data, Computer Use enabled — which would point at a change in the spawn/restart path in 6662 rather than at anyone's local setup.

For whoever picks this up: the identity comparison and the retry policy are separable fixes, and the retry policy is the one that bounds the blast radius no matter which trigger fires. Even with the path comparison left exactly as it is, a single in-flight spawn plus a readiness wait, bounded backoff, process cap and circuit breaker would turn every one of these reports from "app dies in 90–158 s and takes the machine's memory with it" into a logged, recoverable handshake failure.