Remote Control goes offline while desktop WebSocket to chatgpt.com remains active

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

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?

  1. Start Codex.app on macOS.
  2. Use ChatGPT iOS Codex Remote Control to connect to the desktop host.
  3. 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.
  4. Observe that Remote Control works briefly after restarting Codex.app, but later the iOS remote surface shows the host disconnected/offline again.
  5. Restarting Codex.app temporarily 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.

View original on GitHub ↗

11 Comments

github-actions[bot] contributor · 1 month ago

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

  • #24024
  • #23482
  • #22898
  • #22773
  • #23470

Powered by Codex Action

sinoon · 1 month ago

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:

  • the mobile/remote-control surface was offline or disconnected again after a desktop restart had temporarily fixed it;
  • at the same time, the desktop host still had a long-lived Codex Helper -> chatgpt.com:443 connection active for about 11h43m;
  • the connection was still Active, failed=false, and had non-zero traffic;
  • the local desktop logs showed remote-control inventory refresh/reconcile activity, but the remote-visible host state still did not converge back to online.

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.com transport 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.

jshaofa-ui · 1 month ago

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:

  1. Desktop WebSocket: Codex Desktop ↔ chatgpt.com (long-lived, persistent)
  2. Remote Signaling Channel: Separate path for remote control state (likely push notifications or short-lived WebSocket)
┌─────────────────────────────────────────────────────────────┐
│                    Connection Architecture                   │
│                                                             │
│  Desktop Codex ────WebSocket──────→ chatgpt.com             │
│       │                          │                          │
│       │    Remote Signaling      │                          │
│       └────(separate channel)───→│                          │
│                                  │                          │
│                                  ↓                          │
│                         Push/REST API                       │
│                                  │                          │
│                                  ↓                          │
│                         Mobile App (iOS)                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

The remote signaling channel can fail independently of the desktop WebSocket:

  • Push notification delivery failure
  • Remote signaling WebSocket timeout
  • Mobile app backgrounding disconnects the signaling channel
  • Network transition (WiFi ↔ cellular) drops signaling but not desktop WS

Failure Sequence

1. Desktop Codex connects → WebSocket alive ✓
2. Remote signaling channel established → Remote works ✓
3. [Time passes] Mobile app backgrounded / network change
4. Remote signaling channel drops → Mobile shows "offline" ✗
5. Desktop WebSocket still alive → Desktop works fine ✓
6. User sees confusing state: "offline" but desktop functional

Proposed Solution

Architecture: Unified Connection with Cross-Channel Heartbeat

┌─────────────────────────────────────────────────────────────────┐
│                 Unified Connection Architecture                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Desktop Codex                                                  │
│       │                                                         │
│       ├──→ Primary WebSocket (chatgpt.com) ──┐                 │
│       │                                       │                │
│       │    Cross-Channel Heartbeat            │                │
│       │                                       ▼                │
│       └────────────────────────────→ Connection Manager        │
│                                                 │              │
│                                    ┌────────────┼──────────┐   │
│                                    │            │          │   │
│                                    ▼            ▼          ▼   │
│                              Push API    REST API   State Store│
│                                    │            │          │   │
korri123 · 1 month ago

+1 also experiencing this. Mac is not going to sleep, have caffeinate -dimsu on. Just eventually (not after long) disconnects and only solution is to restart Codex on macOS.

sinoon · 1 month ago

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:

  • the desktop-side Codex Helper -> chatgpt.com:443 transport remained established and active;
  • the mobile app, after a fresh restart, still received/rendered the host as offline;
  • therefore the backend-visible Remote Control presence for this host did not converge with the still-active desktop-side transport/session.

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.

sinoon · 1 month ago

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 --verbose style command. For this class of issue, users currently only see a coarse offline / reconnect state, while the useful failure boundary is hidden.

Concrete diagnostics that would make this actionable:

  • whether the desktop host process is running and signed in;
  • whether the desktop remote-control WSS transport is connected, including last ping/pong and last successful server ack time;
  • the current enrollment/session state, with sensitive IDs redacted or shortened;
  • whether the backend currently considers the host online or offline;
  • backend last-seen / heartbeat timestamp for that host;
  • whether a duplicate/stale session exists for the same host/enrollment;
  • the reason the host was marked offline, if known;
  • reconnect attempt count, last reconnect error, close code, and HTTP status if any;
  • a client-visible request/correlation ID when mobile refreshes host presence;
  • whether different client devices are seeing the same backend host state.

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:

  • desktop transport failure;
  • stale enrollment / duplicate session;
  • backend presence/session reconciliation bug;
  • mobile client cache or display bug;
  • account/device binding mismatch.

The additional multi-client observation points again toward backend-visible host presence/session state, not a single mobile client losing its local signaling channel.

sinoon · 1 month ago

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:

  • after restart, the desktop Codex Helper -> chatgpt.com:443 connection can remain Active and failed=false in the local proxy view;
  • earlier investigation saw TCP-level retransmission/backoff behavior on the proxy path, which is consistent with a connection that is not cleanly closed but is no longer healthy;
  • the desktop logs can later enumerate a remote-control:* host but keep its manager state as disconnected;
  • remoteControl/status/read can fail with a local error equivalent to Connection for host ID remote-control:* not found;
  • multiple client devices can still see the same desktop host as offline;
  • restarting Codex Desktop temporarily restores the remote-control surface, which suggests that a fresh process/session registration resets the broken state.

So the current hypothesis is:

  1. A local network/proxy environment may occasionally break or silently drop the long-lived desktop-to-OpenAI Remote Control path without producing a clean close that the app/server handles correctly.
  2. Codex/OpenAI Remote Control does not fully recover from that split-brain state.
  3. The backend-visible host presence can stay offline/disconnected even while the desktop still appears to have an active transport locally.
  4. Restarting the desktop app works because it forces a new app/session/enrollment path, not because the underlying state healed on its own.

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.

sinoon · 1 month ago

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:

  • Surge / local network state still showed active connections to chatgpt.com / ws.chatgpt.com during the period when other clients saw the desktop host as offline.
  • One chatgpt.com:443 connection created right after the Codex.app restart stayed Active / failed=false locally and its byte counters continued to change.
  • The Codex desktop log did not show a clean websocket close/reconnect loop at that point. Instead, shortly after startup it had already logged remoteControl/status/read failing with Connection for host ID <redacted> not found, followed by the remote-control manager state being disconnected.

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.com transport 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.

sinoon · 1 month ago

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:

  1. While the host was shown as offline from other clients, I killed only the desktop Codex app-server child process, without quitting the whole Codex.app.
  2. The Codex.app parent process relaunched the app-server child and the renderer triggered remoteControl/enable again.
  3. The new child reused the persisted remote-control enrollment.
  4. The websocket connection then failed with:
409 Conflict: Remote app server already online

So this appears to be a stale/split remote-control state:

  • OpenAI backend still believes the old remote app server/session is online.
  • The desktop app/server cannot successfully take over by simply restarting the app-server child.
  • Because the persisted enrollment is reused, it keeps hitting the backend's existing-online state instead of creating a clean session.

Then I tried a recovery experiment:

  1. Backed up local Codex state.
  2. Cleared only the local persisted remote_control_enrollments rows.
  3. Restarted Codex.app so it would run a fresh remote-control enable/enroll flow.

After that, the logs showed:

created new remote control enrollment
connected to app-server remote control websocket

A short local observation window after that showed the new Codex connection to chatgpt.com:443 remained Active / failed=false, with bytes continuing to move, and no immediate repeat of the 409 Remote app server already online error.

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:

  • Treat 409 Remote app server already online during remote-control websocket connect as a stale enrollment/session recovery case, not just a terminal failure.
  • Consider clearing/re-enrolling automatically after this error, similar to the existing stale-enrollment recovery path for other cases.
  • Add diagnostics that clearly show: persisted enrollment id/session, websocket lifecycle, backend host presence state, last ping/pong, and whether the backend rejected takeover because it still sees an old server online.
  • Expose a safe “reset/re-enroll Remote Control” action in the app, so users do not have to restart or manually clear local state.

I am intentionally not including account IDs, server/environment IDs, tokens, local IPs, or proxy/node details here.

deadmoto · 1 month ago

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:

  1. Stale remote-control enrollment/state:
  • Clearing only remote_control_enrollments from ~/.codex/state_5.sqlite and restarting codex remote-control produced a fresh enrollment.
  • Logs then showed created new remote control enrollment and connected to app-server remote control websocket.
  1. After that, Android did reach the app-server, but connection still failed:
  • Logs showed an Android client request for thread/list.
  • The request failed with:

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:

chown -R node:node ~/.codex/sessions
chmod -R u+rwX,go-rwx ~/.codex/sessions

After fixing ownership, ChatGPT Android remote-control could connect successfully.

Environment:

  • Codex CLI 0.137.0
  • Linux Docker container
  • codex remote-control
  • persistent ~/.codex
  • ChatGPT Android remote client

This 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.

Lance-dpn · 17 days ago

Adding a Linux daemon + mobile re-login datapoint with a narrower host-side trace.

Environment:

  • Host: Linux 6.8.0-110-generic x86_64 x86_64
  • Codex CLI / managed app-server: 0.142.5
  • Remote surface: ChatGPT mobile app Codex remote control
  • Host app-server is daemon-managed and started as:
/home/<user>/.codex/packages/standalone/current/codex app-server --remote-control --listen unix://

User-visible symptom:

  • The mobile app can still see the device/host.
  • Tapping the visible host does not connect.
  • The failure started after logging out of the mobile app and logging back in.
  • The same account also had another PC move from connectable to not connectable around the same time, so this does not look isolated to one host.

Local state:

codex app-server daemon version
status: running
managedCodexVersion: 0.142.5
cliVersion: 0.142.5
appServerVersion: 0.142.5

The persisted enrollment row exists and is enabled:

websocket_url: wss://chatgpt.com/backend-api/wham/remote/control/server
server_name: ps
server_id: srv_e_6a1a...f2e71d9
environment_id: env_e_6a1a...a9638
remote_control_enabled: 1

The daemon settings also have:

{ "remoteControlEnabled": true }

Important host-side evidence:

  • Local logs show remoteControl/status/changed with status: connected for this environment after daemon restart.
  • The remote-control websocket also receives backend traffic such as ack and ping for the same server id.
  • Therefore the host-side server websocket is not simply offline.

However, when the mobile app tries to connect, there is no new host-side app-server request such as:

app-server request: initialize

Based on the current public code path, a real mobile connection should arrive as a remote-control ClientEvent::ClientMessage containing JSON-RPC initialize, then ClientTracker::handle_message should open a ConnectionOrigin::RemoteControl connection and the app-server logs should show the initialize request. That does not happen here.

So the observed boundary is:

host enrollment enabled: yes
host remote-control websocket connected: yes
backend ack/ping reaching host: yes
mobile can list/see host: yes
mobile connect stream delivered to host websocket: no evidence
host app-server receives initialize after tap: no

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.