Windows + Android Remote Control enters reconnect loop after successful initialize/thread-list
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.
12 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Update: I found a working workaround, and this now looks much closer to #39860 than I initially thought.
What I tested:
vscodethread set had 27 threads with:first_user_messagecharacterspreviewmatchedfirst_user_messageexactly in the state DB, so the previews were effectively unbounded.state_5.sqlite, reducing activevscodethreads from 27 to 2 and active first-message content to only 20,929 characters.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:
archived=1only -> still failsThis 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 original409 Remote app server already onlinemay 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.
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:
initialize;Examples from the affected window:
This continued through many replacement connection IDs (roughly 35-57 in the captured window). Separate log lines also showed
dropping message for disconnected connectionfor some of those retries.Critically, in this regressed state there is no Remote-side
thread/list,thread/resume, or thread hydration afterinitialize. So the current failure happens before the client even gets as far as listing/opening threads.The
thread/resume/thread/turns/listactivity around the same time isConnectionId(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:
initializeand 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.
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:
dropping message for disconnected connectiontwice.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 continuesThat 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.
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:
RESULT: PASSandRESULT: 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:
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.
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
Connected.initialize:dropping message for disconnected connection.thread/listorthread/resumeafterinitialize.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
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.
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
Observed
Connected.initialize.dropping message for disconnected connection.thread/listsession.Local diagnostics
codex.exe app-serverprocess is alive and responsive.codex.ps1 app-server proxyprocesses were found.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.
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:
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 closeloop. 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.
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:
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 presentThis 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.
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 closepattern reported here.Environment
Observed transition (2026-08-23 UTC)
initializeand then closed 340–712 ms later.thread/list.initialize.thread/listarrived on that connection, about 664 ms afterinitialize.config/read,plugin/list,mcpServerStatus/list,skills/list,configRequirements/read, andmodel/list. Android Remote was then fully usable.Control conditions
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.
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
26.818.5229.00.149.0-alpha.4.11.2026.209)Symptoms
test-message workaround stopped working; Android eventually said the task did not exist.Sanitized desktop log sequence
Despite the apparent recovery, more than 24 hours later the same ChatGPT Desktop parent still had two live children with identical command lines:
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:
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:
connectedwhile the active connection has a 409/stale-owner error.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:
409 Remote app server already online.ChatGPT.exeparent 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:
Local paths, PIDs, task/account/install/environment/server IDs, network addresses, and conversation content are omitted.
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:
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.