Windows + Android Remote Control enters reconnect loop after successful initialize/thread-list

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

Summary
Codex Remote Control on Windows is currently unusable from Android.
The issue initially appeared as:
409 Conflict
{"detail":"Remote app server already online"}
That stale-server condition has since been cleared. The Windows app-server now connects successfully to the Remote Control websocket and reports Connected, but Android still cannot establish a usable Remote session.
Versions

  • Codex Desktop: 26.818.31338
  • Bundled app-server: 0.149.0-alpha.4
  • ChatGPT Android: 1.2026.223 (20)
  • Android 16
  • Windows 11 desktop host

Current behavior
On clean startup, Remote Control looks healthy:
remoteControl/status/read ... errorCode=null
remote control websocket status changed ... next_status=Connected
connected to app-server remote control websocket
remoteControl/enable ... errorCode=null
The original 409 is no longer present.
During an Android connection attempt at:
2026-08-21 14:45:07 UTC
the Windows app-server receives:
initialize connection_id=ConnectionId(2) request_id=Integer(10)
thread/list connection_id=ConnectionId(2) request_id=Integer(11)
initialize was received at approximately 14:45:07.080Z and thread/list at 14:45:07.673Z, about 593 ms later.
No local error or connection closed event is logged for ConnectionId(2).
After that, new remote connections are opened. ConnectionId(3) through ConnectionId(9) are created and then immediately closed:
initialize ConnectionId(3)
forwarding remote control connection closed ... ConnectionId(3)
and in some cases:
forwarding remote control connection closed ... ConnectionId(4)
initialize ConnectionId(4)
dropping message for disconnected connection: ConnectionId(4)
dropping message for disconnected connection: ConnectionId(4)
The same pattern repeats for later connection IDs.
On Android, the PC is discovered and shown as connecting/reconnecting, but the session never becomes usable.
Historical comparison
The same logs_2.sqlite contains older healthy Remote sessions where the flow continues normally:
initialize ConnectionId(3)
thread/list ConnectionId(3)
...
connection closed ConnectionId(3)
Other historical sessions also reach:
initialize ConnectionId(4)
thread/list ConnectionId(4)
This shows that the Android client and the app-server are capable of completing the handshake and progressing beyond initialize.
Troubleshooting already performed

  • Full Desktop restart
  • Reinstalled Codex Desktop from Microsoft Store
  • Revoked and re-paired Android
  • Cleared Android cache and full app storage
  • Signed in again and re-paired
  • Backed up Codex state
  • Cleared remote_control_enrollments
  • Removed persisted Remote Control environment/installation keys from global state
  • Rotated the main Codex installation_id
  • Confirmed the new installation identity differs from the previous one
  • Fixed an accidental UTF-8 BOM in .codex-global-state.json and its backup
  • Confirmed clean startup loads global state normally
  • Confirmed Remote websocket is currently Connected
  • Confirmed the original 409 is gone

The problem still reproduces with the fresh installation identity.
Expected behavior
After Android sends:
initialize
thread/list
the Remote session should remain usable and display the host's conversations.
Actual behavior
One connection reaches:
initialize -> thread/list
but never becomes usable on Android.
After that, new remote connections are repeatedly opened and immediately closed during initialize.
Suspected area
This looks like a Remote Control session/relay lifecycle desynchronization rather than a basic pairing, firewall, local enrollment, or host websocket problem.
It appears possible that one side still considers the first Remote connection alive while another part of the Remote Control path starts opening replacement connections.
Related issue

  • #28862 — stale Remote enrollment / 409 Remote app server already online

I can provide sanitized excerpts from logs_2.sqlite with exact timestamps if needed. Account IDs, installation IDs, server IDs and environment IDs are intentionally omitted.

View original on GitHub ↗

12 Comments

github-actions[bot] contributor · 6 days ago

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

  • #39860
  • #39931
  • #39915

Powered by Codex Action

cesariveiro-lang · 6 days ago

Update: I found a working workaround, and this now looks much closer to #39860 than I initially thought.

What I tested:

  • Before changing anything, the active vscode thread set had 27 threads with:
  • 464,660 total first_user_message characters
  • largest message: 50,831 characters
  • 23/27 over 512 chars
  • 18/27 over 8,000 chars
  • preview matched first_user_message exactly in the state DB, so the previews were effectively unbounded.
  • I first marked older threads archived directly in state_5.sqlite, reducing active vscode threads from 27 to 2 and active first-message content to only 20,929 characters.
  • Despite that DB change, Codex Desktop still showed the old threads in Recents, and Remote still failed. This suggests the supported UI/archive path and the raw DB flag are not equivalent for the Remote list/reconciliation path.

Then I archived the old threads from the Codex Desktop UI itself, revoked the Remote connection, and created it again.

Result: Android Remote connected successfully and the recent/project list loaded normally.

So the current A/B result is:

  • Large/unbounded thread set -> Android Remote fails/reconnects
  • Raw SQLite archived=1 only -> still fails
  • Archive old threads through the Codex UI + recreate Remote connection -> Remote connects successfully

This strongly suggests my persistent failure was triggered by the effective Remote thread/list / reconciliation payload rather than basic pairing, firewall, or host WebSocket setup. The original 409 Remote app server already online may have been a separate stale-session issue at the beginning.

This appears closely related to #39860. The detail that direct DB archiving did not change the effective behavior, while archiving through the supported UI did, may be useful when tracing how Desktop builds/reconciles the Remote thread list.

I am leaving this issue open for now because the failure mode included the reconnect/connection-ID churn documented above, but the supported-archive workaround is currently working.

cesariveiro-lang · 6 days ago

Follow-up: the previous workaround was only temporary. Remote has now regressed again, and the new logs narrow the failure further.

After archiving old threads through the supported Codex Desktop UI and recreating the Remote pairing, Android connected successfully for a while. Small threads opened normally, while one very large legacy thread did not hydrate or took an extremely long time. That large thread has ~38.5k rollout items, but total rollout size alone is not explanatory: another legacy thread with a larger total rollout (~201 MB vs ~114 MB) opens successfully on Android.

More importantly, once Remote failed again, the transport pattern changed to an earlier-stage failure:

  • repeated Remote connections are created;
  • each reaches initialize;
  • the Remote connection closes almost immediately;
  • many retries then repeat the same pattern;
  • some responses are later dropped because the connection is already gone.

Examples from the affected window:

initialize connection_id=ConnectionId(35)
forwarding remote control connection closed transport event connection_id=ConnectionId(35)

initialize connection_id=ConnectionId(36)
forwarding remote control connection closed transport event connection_id=ConnectionId(36)

initialize connection_id=ConnectionId(40)
forwarding remote control connection closed transport event connection_id=ConnectionId(40)

initialize connection_id=ConnectionId(46)
forwarding remote control connection closed transport event connection_id=ConnectionId(46)

This continued through many replacement connection IDs (roughly 35-57 in the captured window). Separate log lines also showed dropping message for disconnected connection for some of those retries.

Critically, in this regressed state there is no Remote-side thread/list, thread/resume, or thread hydration after initialize. So the current failure happens before the client even gets as far as listing/opening threads.

The thread/resume / thread/turns/list activity around the same time is ConnectionId(0) and therefore local Codex Desktop stdio activity, not the Android Remote connection. Desktop can parse the large rollout successfully (38544 items, parse errors: 0).

Auth also appears healthy locally (OAuth token refresh returned 200 OK), and pairing/revocation itself succeeds, so this does not look like a basic auth or pairing failure.

Current interpretation:

  1. There is a thread-hydration/performance issue for very large legacy threads when Remote is otherwise working (possibly related to #38653/#38023).
  2. Separately, there is a Remote session/connection-lifecycle failure where Android reaches initialize and the logical Remote connection is immediately torn down, then retried in a loop. This second failure makes Remote completely unusable and is the current blocker.

Because the UI-archive workaround only restored service temporarily, I no longer think #39860 alone fully explains this issue.

cesariveiro-lang · 6 days ago

Further update / correction on timing:

The Remote disconnect did not happen immediately after submitting the prompt. The Codex turn had already been running on the Windows host for at least ~15 minutes when the later Remote failures were captured.

Observed during the already-active turn:

  • Codex Desktop continued executing the task normally on Windows.
  • Android Remote repeatedly lost its logical connection.
  • Remote connection IDs in the captured window closed in succession (for example ConnectionId(62), (63), (64), (65)).
  • For one of those closed connections the host then logged dropping message for disconnected connection twice.
  • The active Codex task itself was not cancelled by the Remote disconnect and continued running on Desktop.

So the stronger interpretation is not "turn/start causes the disconnect". Instead, an already-running task can survive while the Remote interactive session dies independently.

This further separates task execution from Remote session transport/lifecycle:

turn already active on host -> Remote connection closes/reconnects -> some messages target an already-disconnected connection -> host task continues

That seems consistent with stale/replaced connection-ID routing or Remote session lifecycle instability rather than a general Codex Desktop failure.

Also potentially related: #39974 reports very similar Remote instability across Android and iOS while Windows Desktop remains functional, including brief successful connections followed by disconnects and thread-loading failures.

cesariveiro-lang · 6 days ago

Additional observation that may help isolate the failing layer:

After Remote had already become unable to establish a usable interactive session, I closed Codex Desktop and reopened it later. The Android notification shade then showed:

  • a persistent Remote notification saying it was connecting to the Windows host (not connected yet), while
  • separate Codex notifications delivered completed task results such as RESULT: PASS and RESULT: PARTIAL.

So the system can still deliver Codex execution/status notifications while the interactive Remote session remains unavailable.

This suggests the failure is narrower than general host/account connectivity:

  • Codex Desktop execution: working
  • task completion/status delivery / push notifications: working
  • interactive Remote session / relay connection: failing

This also matches the pattern described in #39974, where notifications/approval updates can still arrive while Remote reports the host/session as unavailable.

I cannot prove from the UI alone whether those notifications were queued and flushed on host restart or generated through a separate live path, but the important point is that notification delivery remains functional while Remote itself cannot connect.

staszw · 6 days ago

macOS + Android reproduction with desktop version matrix

I can reproduce the same failure on a macOS host with ChatGPT for Android 1.2026.223 on Android 16.

Observed flow

  • The host Remote WebSocket reaches and remains Connected.
  • Pairing succeeds and the Android device is enrolled.
  • Each Android attempt reaches the host, then the logical connection closes 0–600 ms after initialize:
initialize connection_id=ConnectionId(N)
remoteControl/status/changed targeted_connections=1
forwarding remote control connection closed transport event connection_id=ConnectionId(N)
  • Some races also log dropping message for disconnected connection.
  • In the final reproductions there is no Remote-side thread/list or thread/resume after initialize.
  • One fresh post-reinstall attempt briefly reached thread/list, but later retries regressed to the immediate initialize/close loop.

Desktop version matrix

| ChatGPT Desktop | Bundled app-server | Result |
|---|---:|---|
| 26.803.61601 | 0.147.0-alpha.6.5 | Same initialize/close loop |
| 26.818.22352 | 0.148.0 | Same initialize/close loop |
| 26.818.32112 | 0.149.0-alpha.4.1 | Same initialize/close loop |

Troubleshooting already performed

  • Full desktop and Mac restarts
  • Phone tested on cellular data
  • Server Remote environment recreated after stale-environment detection
  • Fresh QR pairing
  • Mobile cache cleared and app fully reinstalled
  • Pre-reinstall duplicate device grant revoked
  • Newest desktop build and an older desktop build tested

During the final reproductions there was no 401/403, environment-not-found response, queue overflow, or app-server crash. The host Remote WebSocket stayed connected.

This cross-platform reproduction makes a Windows-specific desktop regression less likely and points instead to Android 1.2026.223 or server-side Remote session/protocol compatibility.

The report intentionally excludes account identifiers, installation/server/environment IDs, device model, location, local paths, tokens, and QR data.

Bogrationus · 6 days ago

Additional Windows reproduction on Desktop 26.818.4152.0

I can confirm the same failure on another Windows 11 host after the Desktop package updated from 26.818.3698.0 to 26.818.4152.0.

Client

  • ChatGPT for Android: 1.2026.223
  • Android 16

Observed

  • QR pairing completes successfully and the PC is registered/shown as the linked host.
  • The Windows Remote relay WebSocket reaches and remains Connected.
  • Each Android attempt reaches the Windows app-server and gets as far as initialize.
  • The logical Remote connection then closes immediately; later messages can produce dropping message for disconnected connection.
  • In the failing attempts, Remote does not progress to a usable thread/list session.
  • Codex Desktop itself remains functional.

Local diagnostics

  • The main codex.exe app-server process is alive and responsive.
  • No accumulated duplicate/stale app-server or codex.ps1 app-server proxy processes were found.
  • CPU and memory usage were normal; there was no app-server crash or resource exhaustion.
  • Outbound connectivity to the OpenAI relay/backend is active.
  • Windows Firewall shows no evidence of blocking this flow.
  • A full Windows reboot did not change the behavior.
  • Reinstalling the Android app and completing a fresh QR pairing did not change the behavior.
  • No newer Android app update is currently offered to this device.

This extends the affected Windows build matrix through 26.818.4152.0. Since pairing, enrollment, host relay connectivity, and local app-server health all succeed, while the logical connection dies at/just after initialize, this reproduction is consistent with a Remote session/protocol lifecycle failure rather than a local firewall, pairing, or stale-process problem.

Account IDs, installation/environment/device identifiers, connection IDs, local paths, tokens, and QR data are intentionally omitted.

cesariveiro-lang · 6 days ago

Spontaneous recovery without local changes

At approximately 2026-08-22 01:30–01:31 CEST (2026-08-21 23:30–23:31 UTC), Android Remote became usable again without any new reinstall, re-pairing, restart, state reset, or other local change.

Observed on Android:

  • the Windows host appeared green/connected;
  • the project and recent-thread list loaded;
  • an existing thread opened successfully;
  • the thread content hydrated;
  • live execution events were visible in the thread (planning/tool/command activity);
  • the composer showed the active Windows host and the stop-control for the running turn.

So this was not just host discovery/presence: the interactive Remote session was fully usable again at that moment.

This is useful because the same host/client combination had previously been stuck in the repeated initialize -> immediate close loop. The recovery occurred on its own, with no local remediation between the failing and working states.

I am not claiming this proves a server-side cause by itself, but it strengthens the evidence that the current failure is intermittent/session-lifecycle related rather than a permanently broken local installation or pairing state.

If it drops again, I will capture the narrow host-log window around the transition from working to disconnected.

cesariveiro-lang · 6 days ago

Spontaneous recovery + previously unhydrated large thread now loads

Further update from the same Windows/Android setup:

At approximately 2026-08-22 01:30–01:34 CEST, Remote recovered without any new local intervention between the failing and working states:

  • no reinstall;
  • no re-pairing;
  • no restart;
  • no enrollment / installation-id reset;
  • no thread archiving or payload reduction.

The Android client first showed the host online and loaded projects/recents, then successfully opened existing threads and received live execution state.

More importantly, the large legacy thread that had previously failed to hydrate on Android now loads successfully on the same host and same local data. So thread size / legacy history may still affect performance, but it does not by itself explain the earlier global Remote failure.

There is also a useful partial-connectivity observation from that same large thread: a short user message (Hola) had been sent earlier in the day while Android was still unable to hydrate/render the conversation. Now that Remote has recovered, that message and its reply are present in the persisted history.

That means the earlier broken state was at least partly asymmetric: the mobile->host turn path could succeed and be persisted even while Android history/session hydration was failing.

Current interpretation is therefore stronger:

Remote session broken / hydration unavailable -> some mobile-originated actions still reach host and persist -> later spontaneous recovery -> same large thread hydrates successfully with the earlier turn present

This makes a purely local-corruption or hard thread-size-limit explanation less likely and is more consistent with an intermittent Remote session / relay / synchronization failure.

6950460a-cmyk · 5 days ago

Second spontaneous recovery case: macOS + Android, with the same phone also failing against Windows

I can add another tightly bounded failure-to-success transition that matches the initialize -> immediate close pattern reported here.

Environment

  • ChatGPT for Android: 1.2026.223
  • Android: 16
  • ChatGPT Desktop for macOS: 26.818.41705
  • Bundled app-server: 0.149.0-alpha.4.1
  • During the same incident, the same Android client was unable to attach to both the macOS and Windows hosts, although both hosts remained discoverable/online.

Observed transition (2026-08-23 UTC)

  • 01:06:50–01:09:34: 12 consecutive logical Remote connections reached initialize and then closed 340–712 ms later.
  • None of those failed connections progressed to Remote-side thread/list.
  • 01:09:58.245: the next logical connection reached initialize.
  • The mobile request-number sequence had reset from the high hundreds to a fresh low value.
  • 01:09:58.909: thread/list arrived on that connection, about 664 ms after initialize.
  • 01:10:16–01:10:17: normal follow-up RPCs completed, including config/read, plugin/list, mcpServerStatus/list, skills/list, configRequirements/read, and model/list. Android Remote was then fully usable.

Control conditions

  • The macOS ChatGPT Desktop and app-server processes kept the same PIDs across the failing and working states; neither process restarted.
  • There was no host-side re-pairing, configuration change, or desktop VPN/network change during the transition.
  • The host remained online and the local desktop session continued working.
  • The Android app had already been updated and its cache cleared before the final failed attempts; recovery was delayed, so this observation cannot attribute recovery directly to the update/cache action.

The recovery correlated with creation of a fresh mobile logical session while the host process and host configuration were unchanged. This supports an intermittent Remote relay/session-lifecycle or stale/replaced connection mapping issue more than a persistent macOS/Windows host configuration problem. It is still not possible from host logs alone to distinguish a mobile process/session reset from expiration or reconstruction of server-side session state.

Account, installation, environment, server, device, IP, location, token, QR, and local-path identifiers are intentionally omitted.

ESKehler · 3 days ago

Confirmed Windows failure mode: restart recovery left two live app-server children; removing only the stale child restored Android Remote

I captured a distinct local lifecycle failure that can coexist with the long-thread hydration problem in #38023.

Environment

  • Windows 10 Home 22H2 (build 19045), x64
  • Codex Desktop package 26.818.5229.0
  • Bundled app-server 0.149.0-alpha.4.1
  • Android Remote (mobile version last captured as 1.2026.209)

Symptoms

  • Android reported the Windows host as connected, but this task loaded indefinitely.
  • The composer disappeared; the prior quick test-message workaround stopped working; Android eventually said the task did not exist.
  • The same task existed locally and could be read successfully.
  • Network checks were healthy (DNS and TCP/443), and the Codex processes had established 443 connections.
  • During diagnosis, no Remote-origin task requests were reaching the active desktop renderer.

Sanitized desktop log sequence

2026-08-23T16:04:35.353Z [AppServerConnection] Starting app-server connection hostId=local transport=stdio
2026-08-23T16:05:11.697Z ... cause=initialize_handshake_timeout ... durationMs=35747 ... connectionError={"code":"restart-required"}
2026-08-23T16:05:18.865Z [AppServerConnection] Starting app-server connection hostId=local transport=stdio
2026-08-23T16:05:27.663Z ... failed to connect ... HTTP 409 Conflict ... {"detail":"Remote app server already online"} ... reconnect_attempt=4 ... has_enrollment=true
2026-08-23T16:11:38.396Z [AppServerConnection] Starting app-server connection hostId=local transport=stdio
2026-08-23T16:11:39.789Z ... state=connected error=null
2026-08-23T16:11:44.277Z ... app_server_restart_recovery_done

Despite the apparent recovery, more than 24 hours later the same ChatGPT Desktop parent still had two live children with identical command lines:

codex.exe -c features.code_mode_host=true app-server --analytics-default-enabled

The older child had started at the first connection attempt and was idle in a short CPU sample; the newer child had started at the recovery attempt and was active. Both retained established TCP/443 connections.

Mitigation and result

After re-verifying the process tree, start times, command lines, and activity, I terminated only the older idle app-server child. I did not stop Codex Desktop or the newer active child.

Immediately afterward:

  • exactly one app-server child remained;
  • Codex Desktop remained responsive;
  • local task-list and task-read operations succeeded;
  • the surviving child retained its established 443 connections;
  • Android Remote loaded the previously inaccessible task after a few seconds.

This is a materially successful workaround, though it requires unsafe manual process identification and should not be expected of users.

Likely lifecycle bug

After an initialize timeout/restart recovery, Desktop can start a replacement app-server without reliably reaping or fencing the previous child and its Remote lease. Desktop may then use the replacement locally while Remote traffic/session ownership remains associated with the stale process. The UI can still claim the host is connected.

Suggested fixes:

  1. Reap the previous child during restart recovery.
  2. Fence or transfer the Remote-control lease to the newest child.
  3. Do not present connected while the active connection has a 409/stale-owner error.
  4. Provide a supported repair action that detects and terminates only an orphaned app-server child.
  5. Distinguish this lifecycle failure from Android history hydration/timeouts such as #38023.

This does not establish that the recent Desktop update caused the broader Android issue; Remote hydration was already unreliable before that update. It does establish a duplicate-child failure mode and a successful local mitigation. Task IDs, paths, account details, request IDs, and conversation contents are omitted.

---

Follow-up: exact post-reboot spawn race confirmed

The duplicate-child condition reproduced immediately after a full Windows reboot on newer Desktop package 26.818.8289.0.

Sanitized sequence:

  • ChatGPT Desktop spawned the first app-server.
  • Its initialize handshake timed out after 32.3 seconds.
  • The initialize response arrived only ~237 ms after that timeout, but the child remained alive.
  • Desktop spawned a replacement; it initialized locally, then its Remote websocket received 409 Remote app server already online.
  • A later recovery spawned another managed replacement.
  • A live snapshot afterward still showed the original timed-out child and the newest child, with the same ChatGPT.exe parent and identical app-server command lines. Both retained established HTTPS connections.

This rules out a second Desktop instance or unrelated launcher. The likely race is that a slightly late initialize response crosses the timeout boundary, Desktop loses lifecycle ownership of that child, and restart recovery does not reap or fence it before accepting a replacement.

Two safety/diagnostic notes:

  1. This may be relevant to brief repeated output seen on Android because two app-servers under the same Desktop identity could affect Remote ownership or event reconciliation, but that causal link is not yet proven.
  2. Terminating the sole app-server was tested separately and left Codex unable to start until Windows was rebooted. Any supported repair must positively identify an older duplicate and never terminate the only app-server.

Local paths, PIDs, task/account/install/environment/server IDs, network addresses, and conversation content are omitted.

cesariveiro-lang · 2 days ago

Update on current behavior (2026-08-25): Remote is no longer completely blocking for me, but it remains highly unstable during normal use.

I can now continue working through Android Remote, including following long-running Codex work, but the Remote session frequently drops and reconnects on its own. In many cases it reconnects after several attempts. In other cases I have to back out to the conversation/task list, wait for the Windows host to show as connected again, and then re-enter the task.

Current practical behavior:

  • Remote can establish a usable session.
  • Codex Desktop on Windows continues working while the Android Remote view disconnects.
  • During observation/control from Android, the Remote connection can drop repeatedly and enter short reconnect cycles.
  • Automatic reconnection sometimes succeeds.
  • When it does not, backing out to the list, waiting for the host to reconnect, and reopening the task usually restores access.
  • The instability is recurrent, so Remote is usable but not reliable for maintaining a continuous session.

This is an improvement over the previous state where the reconnect loop could make Remote completely unusable, but I would not consider the issue resolved. The current failure mode looks more like repeated loss/re-establishment of the Remote session during active use than a permanent inability to initialize.

Importantly, I am not currently seeing this as a blocker for the underlying Codex job itself: the Windows/Desktop execution appears to continue while Android loses the Remote session. The main impact now is reliability of the Remote monitoring/control path.

So the severity has changed from "blocked" to "usable with frequent recurrent disconnects/reconnects", but the connection/session lifecycle still appears unhealthy.