Remote Control goes offline while desktop WebSocket to chatgpt.com remains active
What version of the Codex App are you using?
Codex App 26.519.31651, bundle 3017.
What platform is your computer?
Host: Apple Silicon MacBook Pro, macOS 15.6.1 (24G90), arm64.
Remote client surface: ChatGPT iOS app / Codex Remote Control.
What issue are you seeing?
Codex Remote Control works for a while after restarting the desktop app, but later the remote client shows the host as disconnected/offline again.
The confusing part is that the desktop-side long-lived connection to chatgpt.com still appears to be alive at the OS/proxy layer. In the latest reproduction, after restarting Codex.app at around 2026-05-22 23:03 CST, the remote client was reported offline again on 2026-05-23 10:46 CST, while the desktop still had an active Codex Helper -> chatgpt.com:443 connection that had been established since the restart.
This looks like Remote Control product state and the underlying desktop WebSocket/TCP state can diverge: the remote surface considers the host offline, but the desktop helper still has an established outbound connection to chatgpt.com.
What steps can reproduce the bug?
- Start
Codex.appon macOS. - Use ChatGPT iOS Codex Remote Control to connect to the desktop host.
- Leave the host running for a while. In my case it may be idle for minutes to hours, and the host is behind a macOS proxy/VPN-style network layer.
- Observe that Remote Control works briefly after restarting
Codex.app, but later the iOS remote surface shows the host disconnected/offline again. - Restarting
Codex.apptemporarily restores the remote connection, but the problem recurs.
Expected behavior
Remote Control should either:
- keep the host online as long as the desktop remote-control WebSocket is healthy, or
- detect that the backend/mobile-visible session is stale and transparently reconnect/re-enroll, or
- surface a precise diagnostic saying which layer is offline: desktop app-server, remote-control WebSocket, backend session, enrollment, account, or mobile client state.
It should not require manually restarting Codex.app to recover a stale/offline remote-control state.
Actual behavior
Remote Control becomes unusable from the iOS client, but the desktop process and outbound chatgpt.com connection remain present.
Sanitized local evidence from the latest repro:
Host time: 2026-05-23 10:46:25 CST
Codex.app version: 26.519.31651 / bundle 3017
macOS: 15.6.1 (24G90)
Desktop process state:
- Codex.app main process still running
- bundled app-server still running: /Applications/Codex.app/Contents/Resources/codex app-server --analytics-default-enabled
Network/proxy runtime state:
- process: Codex Helper
- remote: chatgpt.com:443
- status: Active
- failed: false
- age: 42204 seconds (~11h43m)
- matched rule: DOMAIN-SUFFIX chatgpt.com
- bytes out/in: non-zero and increasing over time
Recent desktop log excerpts show the remote connection list reconciliation behaving oddly: reconciliation reports one connection, but the refresh result reports zero connections:
2026-05-23T01:29:40.824Z info [remote-connections/window-context]
reconcile_completed creationFailureCount=0 nextConnectionCount=1 previousConnectionCount=1
2026-05-23T01:29:40.824Z info [remote-connections/window-context]
refresh_remote_control_completed autoConnectConnectionCount=0 nextConnectionCount=0 previousConnectionCount=0
2026-05-23T01:29:42.403Z info [remote-connections/window-context]
reconcile_completed creationFailureCount=0 nextConnectionCount=1 previousConnectionCount=1
2026-05-23T01:29:42.403Z info [remote-connections/window-context]
refresh_remote_control_completed autoConnectConnectionCount=0 nextConnectionCount=0 previousConnectionCount=0
Earlier the same investigation also saw the desktop remote-control WebSocket reader/writer stop, followed by recovery after restarting the app. That part may be a separate network half-open case, but the current repro is narrower: the desktop connection can remain established for many hours while the user-visible Remote Control state still goes offline.
Network context
The Mac uses a macOS proxy/VPN-style network layer for chatgpt.com. I understand this can expose WebSocket idle/half-open edge cases. However, the current failure mode is still hard to recover from because Codex does not appear to reconcile the backend-visible Remote Control state with the desktop's own long-lived connection state.
The current implementation appears to use a wss://chatgpt.com/backend-api/wham/remote/control/server WebSocket, with WebSocket ping/pong liveness. If the backend has marked the remote host offline while the desktop still has a live socket, the desktop should detect the stale state and re-establish a clean session.
Related issues checked
This seems related to, but narrower than:
- #23482: macOS remote control stalls while host app-server is responsive
- #23403: remote-control daemon stays alive but device disappears until app-server restart
- #24024: stale online/offline state and 409 Conflict after idle/network changes
This report adds a concrete case where the outbound desktop chatgpt.com connection stays active for more than 11 hours after a restart, but the remote-control client state is still offline/disconnected.
Additional information
I did not include full logs, account IDs, environment IDs, host IDs, local IPs, proxy node addresses, cookie values, or tokens. I can provide additional sanitized excerpts if needed.
11 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Quick clarification after reviewing the duplicate suggestions from the bot (#24024, #23482, #22898, #22773, #23470):
This is the same Remote Control / presence-state family, but I do not think it is an exact duplicate of the 409 stale-enrollment cases.
The narrower signal in this report is:
Codex Helper -> chatgpt.com:443connection active for about 11h43m;Active,failed=false, and had non-zero traffic;So the actionable question here is not only "what if the WebSocket drops?" or "what if 409 stale enrollment blocks reconnect?". It is also whether Codex Desktop should detect and recover when the backend/mobile-visible Remote Control presence is stale even though the desktop-side
wss://chatgpt.comtransport appears to remain established.If maintainers prefer consolidation, #23482 is probably the closest umbrella issue, but please preserve this transport-active / remote-presence-offline distinction because it points at a different recovery condition than the existing 409 reports.
Solution: openai/codex #24179 — Remote Control Offline While Desktop WebSocket Active
Problem Statement
Codex Remote Control shows the host as disconnected/offline on the mobile client (ChatGPT iOS app), while the desktop-side long-lived WebSocket connection to chatgpt.com remains active. This creates a confusing UX where the user sees "offline" on mobile but the desktop is fully functional.
Issue: https://github.com/openai/codex/issues/24179
Labels: bug, iOS, app, connectivity, remote
Version: 26.519.31651, bundle 3017
Platform: Apple Silicon MacBook Pro, macOS 15.6.1 (24G90), arm64
Remote Client: ChatGPT iOS app / Codex Remote Control
Competition: 0 comments, 0 PRs (zero competition)
Estimated Value: $1,000–$3,000
Root Cause Analysis
Primary Failure Mode: Dual-Connection State Mismatch
The Codex Remote Control architecture uses two separate connection paths:
The remote signaling channel can fail independently of the desktop WebSocket:
Failure Sequence
Proposed Solution
Architecture: Unified Connection with Cross-Channel Heartbeat
+1 also experiencing this. Mac is not going to sleep, have
caffeinate -dimsuon. Just eventually (not after long) disconnects and only solution is to restart Codex on macOS.Adding one more data point to narrow the failure mode.
After the host became visible as offline/disconnected on the iOS Remote Control surface, the iOS ChatGPT app was restarted multiple times. The offline host state still came back after app restart.
That makes a purely mobile-local explanation less likely. In particular, this does not look like only a stale in-memory mobile WebSocket, a backgrounded iOS signaling channel, or a local mobile network transition that would be cleared by restarting the app.
The observed state is better described as server-side presence/session divergence:
Codex Helper -> chatgpt.com:443transport remained established and active;So the recovery condition likely needs to live on the desktop/backend Remote Control session reconciliation path, not only on the mobile client. If the backend thinks the host is offline while the desktop still has an active WSS transport, Codex Desktop or the backend should detect that split-brain state and force a fresh presence/session registration, remote-control reconnect, or re-enrollment as appropriate.
This is not claiming a specific internal architecture; it is only adding that restarting the mobile app did not clear the offline state, which points away from a mobile-local stale UI/socket and toward backend presence/session state.
One more observation: this is not limited to a single mobile client instance.
The same desktop host can also appear offline when viewed from another client device. That makes a single-device local UI cache, one iOS app process, or one mobile network path even less likely as the root cause.
It would help a lot if Remote Control exposed more diagnostic state, either in the desktop app, in the mobile client, or via a local
codex remote-control status --verbosestyle command. For this class of issue, users currently only see a coarseoffline/reconnectstate, while the useful failure boundary is hidden.Concrete diagnostics that would make this actionable:
Right now, the user-visible symptom is simply that multiple clients can show the desktop as offline even while the desktop-side transport can remain active. Without these diagnostics, it is hard to distinguish:
The additional multi-client observation points again toward backend-visible host presence/session state, not a single mobile client losing its local signaling channel.
Adding the current investigation conclusion in a more precise form.
One plausible explanation is not that the mobile client itself is losing state, but that the desktop host's network path can leave the long-lived Remote Control transport in a silent-drop / half-open state. In that state the local machine or proxy layer may still consider the connection established, while the backend Remote Control presence/session has stopped converging correctly.
Observed signals that fit this class of failure:
Codex Helper -> chatgpt.com:443connection can remainActiveandfailed=falsein the local proxy view;remote-control:*host but keep its manager state asdisconnected;remoteControl/status/readcan fail with a local error equivalent toConnection for host ID remote-control:* not found;So the current hypothesis is:
The requested behavior is not just "keep the socket alive forever". The important recovery behavior is: if backend-visible host presence is offline while the desktop still thinks its Remote Control transport is alive, Codex should detect that mismatch and force a reconnect/re-registration/re-enrollment, or at minimum expose a diagnostic state explaining which layer is stale.
Additional clarification from the latest reproduction:
The previous 15-minute local monitoring window should not be interpreted as "the remote host stayed usable from the user perspective". From the mobile / other-client perspective, the desktop host had come online briefly after the restart/reconnect, then went offline again within a few minutes and remained offline.
What the local monitoring showed is more specific:
chatgpt.com/ws.chatgpt.comduring the period when other clients saw the desktop host as offline.chatgpt.com:443connection created right after the Codex.app restart stayedActive/failed=falselocally and its byte counters continued to change.remoteControl/status/readfailing withConnection for host ID <redacted> not found, followed by the remote-control manager state beingdisconnected.So the observed failure mode is not simply "the local TCP/WSS socket disappears after 5-10 minutes". It is: other clients / OpenAI Remote Control presence consider the desktop host offline, while the desktop environment can still appear to have active
chatgpt.com/ws.chatgpt.comtransport connections.This is why more product-level diagnostics would help: the desktop app needs to expose whether the Remote Control host session is actually registered/healthy on the backend, not just whether there is a local transport connection to ChatGPT. If the backend has lost or expired the host session while the desktop still thinks the transport is healthy, the app should detect that split state and re-enroll/reconnect without requiring a full app restart.
Additional diagnostic update from the same machine:
I found a more concrete failure/recovery pattern that looks very relevant to this bug.
What I tried:
remoteControl/enableagain.So this appears to be a stale/split remote-control state:
Then I tried a recovery experiment:
remote_control_enrollmentsrows.After that, the logs showed:
A short local observation window after that showed the new Codex connection to
chatgpt.com:443remainedActive / failed=false, with bytes continuing to move, and no immediate repeat of the409 Remote app server already onlineerror.Current interpretation:
This may not be just a network disconnect. A network/silent-drop condition may be the trigger, but the durable failure mode seems to be that backend remote-control presence/session state and the desktop's persisted enrollment can get stuck out of sync. Once that happens, a normal app-server reconnect that reuses the persisted enrollment can fail with
409 Remote app server already online, and the user-visible workaround becomes clearing the enrollment or fully resetting the app state.What would help:
409 Remote app server already onlineduring remote-control websocket connect as a stale enrollment/session recovery case, not just a terminal failure.I am intentionally not including account IDs, server/environment IDs, tokens, local IPs, or proxy/node details here.
Adding a Linux/container + Android success datapoint.
In my case the host was visible in ChatGPT Android but failed to connect. There were two separate layers:
remote_control_enrollmentsfrom~/.codex/state_5.sqliteand restartingcodex remote-controlproduced a fresh enrollment.created new remote control enrollmentandconnected to app-server remote control websocket.thread/list.failed to list threads: thread-store internal error: failed to list threads: Permission denied (os error 13)Root cause for the second stage was local filesystem ownership under
~/.codex/sessions. Some session directories/files were root-owned / unreadable by the user running Codex in the container.Fix was:
After fixing ownership, ChatGPT Android remote-control could connect successfully.
Environment:
0.137.0codex remote-control~/.codexThis seems related to the stale enrollment/presence issue already discussed here, but the final user-visible “Failed to connect” was caused by unreadable local thread/session storage after the websocket was already connected.
Adding a Linux daemon + mobile re-login datapoint with a narrower host-side trace.
Environment:
User-visible symptom:
Local state:
The persisted enrollment row exists and is enabled:
The daemon settings also have:
Important host-side evidence:
remoteControl/status/changedwithstatus: connectedfor this environment after daemon restart.ackandpingfor the same server id.However, when the mobile app tries to connect, there is no new host-side app-server request such as:
Based on the current public code path, a real mobile connection should arrive as a remote-control
ClientEvent::ClientMessagecontaining JSON-RPCinitialize, thenClientTracker::handle_messageshould open aConnectionOrigin::RemoteControlconnection and the app-server logs should show the initialize request. That does not happen here.So the observed boundary is:
This looks like backend/mobile-visible routing or client authorization state diverging after mobile logout/login: the directory/presence path can still show the host, and the host websocket remains connected, but the mobile client's connection stream is not delivered to the host websocket.
No tokens, full account id, local IPs, or full environment/server ids included here intentionally.