Remote Control cannot attach to a live CLI session on the workstation (completed threads work, in-progress ones fail)
What variant of Codex are you using?
CLI + ChatGPT iOS Remote Control (paired hosts)
What feature / fix would you like?
Support the normal workstation-primary workflow:
- Start and run the task in Codex CLI on the workstation
- Use the phone only to monitor progress, approve actions, and send short steering replies
- Return to the same live CLI session without quit/resume or forking
Today, Remote Control effectively allows the phone to open completed threads, but fails when the workstation CLI already owns a live / in-progress session.
Why this matters
Almost nobody’s main coding surface is the phone. The realistic loop is:
- keyboard + terminal on the workstation
- phone as a second screen for status + decisions while away from the desk
Claude Code Remote Control (/rc) matches this model: the local CLI session stays primary; mobile is another window into the same live session.
Codex Remote currently behaves more like: “pair a host → operate threads from mobile,” and does not reliably share an already-attached live CLI session with iOS.
What happens today (observed)
| Session state | iOS Remote result |
|---|---|
| Completed (no active CLI attach) | Opens and reads normally |
| Live / in-progress CLI session | Fails to load |
On the failing live session, iOS shows:
加载消息时出错:Codex 服务器返回了错误。
(“Error loading messages: Codex server returned an error.”)
此任务无法重新连接
(“This task cannot reconnect”)
So the phone can act as a history viewer for finished threads, but cannot attach to the live session the CLI already owns.
Expected behavior
- Workstation CLI remains the primary client
- iOS can attach to that same live thread as a secondary client
- Monitoring, approvals, and short follow-ups work without forcing the user to start the task from mobile
- Returning to the workstation TUI does not require quitting and
resume, and does not create divergent continuations
Actual behavior
- Completed threads: OK from iOS
- Live CLI-attached threads: iOS cannot reconnect / server error
- Practical workaround pushed by the product shape: start work from Remote/App side instead of CLI — awkward for workstation-first developers
Suggested direction
Any of:
- Explicit multi-client live attach (CLI + mobile on the same thread)
- Or a clear “monitor/approve-only” secondary session mode for Remote
- Or document and enforce single-writer rules without hard-failing the phone on live threads (e.g. read-only view + queued approvals)
Related: #32445, #34632, #23011
Additional information
_No response_
11 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
I can reproduce this exact iOS Remote error on macOS, with a more specific discriminator.
iOS displays:
Error loading messages: Codex server returned an error.
This suggests the boundary may be paginated-history handling in the mobile/cloud Remote path, rather than only whether the CLI thread has an active writer.
iOS ChatGPT version: 1.2026.209
I can provide a feedback/session ID and sanitized logs privately.
Crazy this thing isn't implemented; and its partial impl is so flawed: control commands (to view/compact context for example) do not work from mobile
Your completed-vs-live table matches a specific mechanism in the thread store: cross-process writer locks. Every live thread holds an exclusive lock file under
$CODEX_HOME/thread-writer-locks/(https://github.com/openai/codex/blob/1f41cc5d92/codex-rs/thread-store/src/local/writer_lock.rs#L17-L34). The workstation TUI's process owns that lock for the in-progress session; when the Remote Control daemon — a different process — tries to open the same thread for iOS, the acquisition fails and surfaces as the generic "Codex server returned an error." Completed threads hold no writer lock, so they open fine. The same single-writer constraint is what blocks/fork-then-resume in another terminal (#38144, #38297) and produces the empty two-instance replay in #37987.So "phone as second screen on the live session" needs one of: (1) a read-only attach path that never takes the writer lock — enough for monitoring, and probably the quick win; (2) routing mobile steering/approvals through the process that owns the lock (the app-server multi-client machinery exists; the remote-control daemon would proxy into the owning session rather than opening the thread itself). Option 2 is the Claude-
/rc-style behavior you're describing; option 1 could ship first and already covers monitoring.I can reproduce a closely related paginated-history failure on Android, with the important distinction that live steering still works: the phone can send a new message and receive the reply, but opening the thread is slow and the beginning of the conversation is not rendered.
Environment:
codex-cli 0.149.0codex app-server --remote-control --listen unix://, directly supervised by a user systemd service1.2026.230history_mode = paginatedThe history is intact and locally readable. I queried the running app-server over its Unix WebSocket:
| Request | Result | Local latency | Encoded response |
|---|---:|---:|---:|
|
thread/turns/list, ascending,itemsView: summary| 5 turns,nextCursor: null| 5 ms | 12,714 bytes ||
thread/turns/list, ascending,itemsView: full| the same 5 turns,nextCursor: null| 46 ms | 3,104,863 bytes ||
thread/read,includeTurns: true| the same 5 turns | 56 ms | 3,106,375 bytes |The returned turn statuses are
interrupted,completed,completed,completed,inProgress. The first user messages and assistant replies are present in both the rollout andthread_history_1.sqlite; the projection byte offset is current. There is no host-side read error in the app-server journal.This produces two user-visible symptoms:
The large full response can explain some latency, but not why the beginning is absent. The 12 KB summary response contains enough information to enumerate every turn, so this looks consistent with a mobile/cloud Remote paginated-history handling problem rather than lost host data or a writer-lock failure.
This is still reproducible on Android app
1.2026.230, newer than the iOS build reported above. Happy to provide sanitized protocol output if useful.Confirmed temporary workaround on Fedora:
A live test against the 0.149 server created a normal user thread with history_mode=legacy, while the server remained connected to Android. The server records cli_version=0.149.0 because it owns the thread, but the history contract is legacy.
This matches the tagged source: the 0.149 TUI explicitly sends ThreadHistoryMode::Paginated for non-ephemeral new threads, while 0.146 leaves history_mode unset and the app-server default remains legacy.
This does not convert existing paginated threads, but it provides a workable CLI-first path for newly created threads while retaining the current Remote server and its mobile connection.
This happens also to me with android, macbook and cmux.
Same issue, on my macbook iTerm session and iphone app.
I reproduced the same issue with VS Code + iPhone Remote Control: an active thread owned by VS Code cannot currently be opened on the iPhone, even for viewing. I added OS-level writer-lock evidence and a proposed read-only observer + “Take over here” handoff flow in #40157.
A workaround shaped differently. If you start the task as
shell codex, the live PTY is exactly what the phone gets, there is no separate attach step or writer lock to fight, the browser link and the workstation terminal are the same session. Disclosure, I work on Pilot Protocol, which develops shell.online. The limitation, it only helps for sessions launched through it, it cannot adopt an already-running bare codex.Status check after the
0.150.0/0.150.1releases:1.2026.230says long-thread loading was improved and older history is now fetched as needed.0.150.0/0.150.1CLI notes do not mention Android long-thread hydration, read-only Remote attach, writer-lock handoff, or stale-lock lifecycle. The listed0.150.1fix concerns retained-image budgeting during Remote compaction.Could a maintainer clarify:
0.150.xinclude any relevant unlisted fix, or should affected users continue using legacy-history workarounds?