Repository navigation
[macOS][regression] codex_app MCP override makes CODEX_APP_SERVER_USE_LOCAL_DAEMON=1 unreachable #41014
Description
Summary
After updating ChatGPT Desktop, the existing managed local App Server path no longer works in a normal Desktop startup.
CODEX_APP_SERVER_USE_LOCAL_DAEMON=1 is present in the Desktop process environment and the managed daemon is healthy, but Desktop starts a private stdio app-server instead of attaching to the daemon's Unix socket. If another local client is already using the same thread through the managed daemon, Desktop then shows:
This is open in another app
Close it there to continue here.
This appears to be a startup-logic regression caused by the new app-managed codex_app MCP override.
Environment
- ChatGPT Desktop:
26.820.60940(build7119) - Bundled Codex:
codex-cli 0.150.0-alpha.8 - Standalone managed Codex/App Server:
0.149.1 - Bundled
codex-app-toolsplugin:0.1.3 - macOS:
26.5.2(25F84) - Architecture: Apple Silicon /
arm64
Managed daemon status before launching Desktop:
{
"status": "running",
"backend": "pid",
"managedCodexVersion": "0.149.1",
"socketPath": "$CODEX_HOME/app-server-control/app-server-control.sock",
"cliVersion": "0.150.0-alpha.8",
"appServerVersion": "0.149.1"
}Previously working behavior
Before the Desktop update, this setup worked:
ChatGPT Desktop ────────┐
├─ managed app-server --listen unix://
local sidecar/client ───┘
Both clients connected to the same Unix socket and could observe/continue the same live thread.
This is the same shared-daemon topology documented in #31991.
Steps to reproduce
-
Install the standalone Codex package.
-
Start the managed App Server:
codex app-server daemon bootstrap
-
Ensure the Desktop process receives:
launchctl setenv CODEX_APP_SERVER_USE_LOCAL_DAEMON 1
-
Fully quit and restart ChatGPT Desktop.
-
Connect a second local client to
$CODEX_HOME/app-server-control/app-server-control.sock. -
Open or continue a thread through that client.
-
Open the same thread in ChatGPT Desktop.
Actual behavior
Desktop does not attach to the managed daemon. It uses a private stdio app-server, so the system has two app-server processes with separate writer ownership:
ChatGPT Desktop ── private app-server --listen stdio://
local client ───── managed app-server --listen unix://
Opening the managed-daemon thread in Desktop produces the active-writer / “open in another app” conflict.
Fully quitting Desktop releases its private writer, but that defeats the shared-client workflow.
Diagnostic evidence
Inspection of the packaged Desktop startup logic shows that the managed-daemon branch is selected only when all of the following are true, including:
configOverrides.length === 0 &&
process.env.CODEX_APP_SERVER_USE_LOCAL_DAEMON === "1"The same startup now creates the Codex app-tools native pipe, sets CODEX_APP_TOOLS_PIPE_PATH, reads the bundled codex-app-tools/desktop-mcp.json, and returns a request-level override for:
mcp_servers.codex_app
Therefore, on a healthy normal Desktop startup:
codex_app native pipe starts
→ mcp_servers.codex_app override is non-empty
→ configOverrides.length !== 0
→ managed-daemon branch is skipped
→ private stdio app-server is spawned
The daemon version gate is not the blocker: the packaged minimum is older than 0.149.1, so this execution path is rejected before daemon version selection matters.
The current Desktop process environment was also checked:
CODEX_APP_SERVER_USE_LOCAL_DAEMON=set
CODEX_APP_TOOLS_PIPE_PATH=set
CODEX_CLI_PATH=unset
CODEX_APP_SERVER_FORCE_CLI=unset
CODEX_APP_SERVER_WS_URL=unset
Expected behavior
If CODEX_APP_SERVER_USE_LOCAL_DAEMON=1 is set and a compatible daemon is healthy, Desktop should attach to it.
At minimum, one of these should be supported:
- Give the explicit local-daemon setting precedence and disable Desktop-only app tools with a clear warning.
- Provide a supported setting to disable the app-managed
codex_appMCP injection. - Make the
codex_apptools client/session-scoped so they do not require a process-wide app-server config override. - Support per-connection config overlays on the managed daemon.
Why this is distinct from a generic writer-lock issue
The writer conflict is the visible symptom. The earlier transport-selection decision is the regression: an automatically injected Desktop MCP override makes the existing local-daemon branch unreachable during normal startup.
Related reports cover the individual symptoms, but not this combined cause:
- Support Desktop using the managed local App Server for direct mobile SSH/Tailscale control #31991 — previously working Desktop + managed App Server topology
- [macOS][regression] Desktop cannot resume Remote Control / CLI thread:
already has an active writerafter latest update #37403 — Desktop/CLI/Remote thread getsalready has an active writerafter update - Feature request: idempotent task creation and explicit phone ↔ Codex Desktop ownership handoff #40157 — standalone app-server/Remote → Desktop produces “opened in another app”
- Codex Desktop: invalid transport in mcp_servers.codex_app although codex_app is not present in config.toml #40860 — Desktop injects
mcp_servers.codex_appeven when absent from user config - Desktop app 26.820.7780.0: resuming WSL-hosted threads fails with "invalid transport in mcp_servers.codex_app" #40819 — request-level
codex_appconfig overrides file-level configuration
Additional note
I understand that CODEX_APP_SERVER_USE_LOCAL_DAEMON is undocumented. However, the current Desktop bundle still contains the environment-variable check, daemon discovery/version gate, and Unix-socket transport path. The automatic codex_app override now prevents that retained path from being selected.
No thread IDs, usernames, repository names, or private paths are included.
Activity
github-actions commentedon Aug 27, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Zollicoff commentedon Aug 31, 2026
Confirmed this still reproduces on the current macOS Desktop/SSH stack, with the same root cause described here.
Current environment
26.825.51511(build7377)0.151.0-alpha.7.20.151.0Exact topology
The local Desktop starts its private bundled server:
The Desktop's saved SSH connection independently bootstraps the managed server:
Both processes use the same Codex home and thread store.
Reproduction and lock ownership
thread/resumerepeatedly fails with:lsofconfirms the Desktop-bundled app-server holds the correspondingthread-writer-locks/<redacted>.lockfile.The direction is symmetric. After the local Desktop releases ownership and the SSH app-server resumes the thread,
lsofshows the SSH app-server holding that same lock; the local Desktop then becomes the side that cannot resume it.This was reproduced on two different remote hosts. No
CODEX_HOME,CODEX_CLI_PATH,CODEX_APP_SERVER_WS_URL,CODEX_APP_SERVER_FORCE_CLI, orCODEX_SSH_SKIP_APP_SERVER_BOOToverride is required.Packaged-code confirmation
The current Desktop bundle still contains the local-daemon transport path guarded by
CODEX_APP_SERVER_USE_LOCAL_DAEMON=1. It also still requires the request-level config override list to be empty before selecting that path.A normal Desktop launch injects both
features.code_mode_host=trueand the app-managedmcp_servers.codex_appoverride. Consequently, the override list is non-empty and Desktop selects a private stdio app-server instead of the healthy managed daemon socket.The managed daemon itself is healthy and version-compatible:
{ "status": "running", "managedCodexVersion": "0.151.0", "cliVersion": "0.151.0-alpha.7.2", "appServerVersion": "0.151.0" }Impact
This breaks the intended host workflow:
The thread can be listed and its metadata can be discovered, but only the app-server that currently owns the writer lock can resume and operate it. Fully quitting one surface merely transfers which side can work; it does not provide shared access.
The exclusive writer lock appears to be functioning correctly. The missing piece is either:
codex_apptools connection/session-scoped so Desktop can share the managed daemon;No thread IDs, hostnames, usernames, repository names, or private filesystem paths are included.
beifangzhishi-ops commentedon Sep 1, 2026
I can reproduce a closely related
codex_app/ shared App Server failure on Windows, but through a different transport path: an externally launched/shared Codex App Server over WebSocket rather than the managed local-daemon path described here.My setup is:
and Codex Desktop is pointed at it with:
In that topology, Desktop does connect to the external/shared App Server, but both
thread/startandthread/resumefail with:Important observations:
[mcp_servers.codex_app]entry in my userconfig.toml.[desktop] runCodexInWindowsSubsystemForLinux = falsedoes not affect this reproduction; this path is not using WSL.So this appears related to the same broader design boundary described in this issue: Desktop-side runtime injection of
mcp_servers.codex_appseems to assume an App Server lifecycle/transport managed directly by Desktop, and shared/external App Server topologies can break when that assumption is not true.The visible failure differs from #41014 — here the shared WebSocket server is selected, but the request-level
codex_appconfiguration becomes invalid — while #41014 shows the override preventing the shared daemon path from being selected at all. That makes these look like two manifestations of the samecodex_appruntime-injection architecture problem across different shared App Server transports.ScriptedAlchemy commentedon Sep 15, 2026
Additional reproduction notes (2026-09-15)
Observed on both Mac Studio (ChatGPT Desktop / Codex Framework ~152.x,
codex-cli 0.154.0) and ubuntu-main:codex app-serverwithplugins.codex-app-tools@openai-bundled.mcp_servers.codex_app.enabled=trueandCODEX_APP_TOOLS_PIPE_PATH=/tmp/codex-browser-use/<uuid>.sock.~/.codex/app-server-control/app-server-control.sockis absent while Desktop is running (private stdio app-server), so external clients cannotturn/startinto live Desktop threads via the documented unix transport.send_message_to_threadon that temp pipe — not a public app-server RPC.We are tracking a Grok Bot ↔ Codex bridge that needs shared-daemon attach for Grok→Desktop inject:
Happy to provide more process/env dumps if useful.
fistashka05 commentedon Sep 15, 2026
Still reproducible on ChatGPT Desktop 26.908.40834.
I independently reproduced the managed-daemon attachment regression on macOS.
What I verified:
initialize;CODEX_APP_SERVER_USE_LOCAL_DAEMON=1is set before a full ChatGPT Desktop restart and is inherited by the restarted Desktop process;Observed topology:
Expected topology:
I also tested the MCP side independently. Dynamic external MCP operation itself works when driven through the Desktop App Server:
config/mcpServer/reload, thread-scoped MCP status, model-visible tool mount, one real model-initiatedtools/call, disable/reload, and cleanup all succeeded with no leak.The remaining blocker is specifically the Desktop attachment/control-plane boundary: a
UserPromptSubmithook or local sidecar cannot reach the UI-owned private App Server for same-turn MCP activation through the supported managed-daemon socket because Desktop does not attach to that daemon.No unsupported process/FD injection, app-bundle patching,
CODEX_CLI_PATHoverride, or manual MCP proxy was used. Test state was restored after reproduction.This appears to confirm that the
codex_appoverride / private-stdio selection regression described in #41014 is still present in Desktop 26.908.40834.Pvxlabs commentedon Oct 11, 2026
Current macOS reproduction and read-only runtime observation requirement
Adding sanitized evidence to this existing issue rather than opening a duplicate. The transport-selection root cause matches #41014. The narrower requirement below is independent, read-only observation of an existing Desktop-owned turn while retaining plugins; it does not require an external client to resume or mutate the thread.
Component and environment
Codex Desktop on macOS 26.6.2, arm64.
Desktop version: 26.1007.30912, build 20156.
Bundled CLI/app-server: 0.162.0-alpha.17.2.
A separately configured managed daemon uses 0.162.1.
Problem
An independent read-only monitoring application cannot obtain authoritative current status for an already-running Desktop-owned thread when Desktop uses a STDIO Owner with plugin configuration overrides.
Observed for the same exact thread T and current turn R:
The monitor therefore correctly keeps this session Unknown instead of claiming Running. Reading historical RUNNING fields or repeating a historical status with a new timestamp would manufacture liveness.
Request
Please confirm whether a supported independent, authenticated, read-only Desktop Owner status API already exists. If not, expose one that works for existing STDIO-owned Desktop sessions without disabling plugins, changing Desktop binaries, restarting active sessions, or taking over the thread.
Required minimum capabilities:
An official local-only transport or SDK export is sufficient. We are not requesting access to an executor's private injected App Tools pipe or caller credentials, and are not proposing Desktop stdout interception.
Reproduction without publishing private session data
This report documents a specific installed configuration, not a claim that all Desktop builds select STDIO whenever any override exists.
Acceptance criteria for a complete solution
Privacy
This report intentionally omits real thread/turn IDs, cwd/socket paths, project names, private logs, credentials, session transcripts and screenshots. Additional diagnostics should only be shared through an explicitly authorized channel after review.
nilswx commentedon Oct 14, 2026
Current macOS confirmation (2026-10-14)
I reproduced the same ownership split with the current installed CLI/app-server
0.162.1:codex agentsstarts/uses the separate Machine-managed Unix-socket daemon for the same.codexhome.lsofshows anonymous socketpairs for the Desktop child, no TCP listener, and a separate named socket for the managed daemon.No process was restarted or signaled, and no writer lock was altered. This confirms the reported external-client limitation still affects the current CLI generation; the supported Desktop owner endpoint / daemon-attachment path remains the missing capability.