Desktop: chat disappears after restart when one host is connected through both SSH and Remote Control

Open 💬 5 comments Opened Aug 17, 2026 by ckstar069
💡 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 (From “About Codex” dialog)?

26.810.52044 (build 6662)

The bundled local app-server/CLI was 0.148.0-alpha.9 when I collected the diagnostics below.

What subscription do you have?

Pro

What platform is your computer?

Local Mac:

Darwin 25.5.0 arm64 arm

Remote machine:

Ubuntu 24.04
Linux 7.0.0-28-generic x86_64

The SSH app-server on the remote machine reported 0.144.1 during the incident. It reported 0.147.0 after Codex Desktop restarted and updated the managed copy.

What issue are you seeing?

I use Codex Desktop on my Mac as the UI for a project stored on a Linux machine. I had added that Linux machine to Codex in two ways:

  • as an SSH project;
  • through Remote Control under Control other devices.

Both entries referred to the same physical machine and the same remote CODEX_HOME.

I created a chat while working in the SSH project. The next day I upgraded/restarted Codex on the Linux machine. When I reopened Codex Desktop on the Mac, the SSH project was still in the sidebar, but the chat was gone.

The chat had not actually been deleted. Its rollout file was still on the Linux machine. The file was about 1.15 MB, contained 342 valid JSONL records, and had no JSON parse errors. I could also read the complete chat directly from the remote app-server with thread/read and thread/turns/list.

While investigating, I found the same thread ID twice in Codex Desktop's local thread catalog:

host_id                                      thread_id          cwd
remote-ssh-codex-managed:<ssh-alias>         <same-thread-id>   /home/<user>/Repo/<project>
remote-control:<redacted-environment-id>     <same-thread-id>   /home/<user>/Repo/<project>

The two rows had the same title, timestamps, working directory, and source. The two copies also showed no project association (projectId: null) when listed through the Desktop task API.

The Desktop log shows both connections starting during the same launch:

Starting app-server connection hostId=remote-ssh-codex-managed:<ssh-alias> transport=websocket
Starting app-server connection hostId=remote-control:<redacted-environment-id> transport=websocket

It also contains this error while the SSH connection was starting:

Failed to load config requirements errorMessage="No AppServerRequestClient registered for hostId: remote-ssh-codex-managed:<ssh-alias>"

After I removed the duplicate Remote Control catalog data and restarted the app, the chat title came back. At first, opening it still showed a blank page even though the app-server could return the full chat. The content appeared only after the thread was explicitly opened/read using the SSH host identity.

What steps can reproduce the bug?

These are the steps that led to the problem on my machines. I have not repeated them with a clean profile because I do not want to risk my existing remote chat history.

  1. Add a Linux machine to Codex Desktop through Connections > Control other devices.
  2. Add the same machine as an SSH project.
  3. Open a project through the SSH entry and create a chat.
  4. Confirm that the rollout is stored under ~/.codex/sessions/ on the Linux machine.
  5. Leave both connection methods configured for the same machine.
  6. Upgrade or restart Codex on the Linux machine, then quit and reopen Codex Desktop on the Mac.
  7. Open the saved SSH project.

In my case, the existing chat was no longer shown. During recovery, the title later appeared with an empty conversation body even though direct app-server reads still returned the messages.

I have redacted the affected thread ID from this public report, but I can provide it privately if it would help with diagnosis.

What is the expected behavior?

Restarting or upgrading either side should not make a persisted remote chat disappear or open as an empty conversation.

If SSH and Remote Control can be used with the same machine and CODEX_HOME, Codex Desktop should keep the host and project association when it lists and opens a thread. If that setup is not supported, the app should warn the user instead of adding the same thread under two host identities.

Additional information

My best guess is that the upgrade/restart exposed an existing identity conflict rather than causing data loss. The Desktop catalog uses (host_id, thread_id) as its key, so the same thread ID can exist once under the SSH host and once under the Remote Control host. Some later lookup or UI path appears to expect one result or to lose track of which host was selected.

I also found an old standalone app-server process on the Linux machine. It may have made the restart behavior worse, but I do not think it explains why the Desktop catalog had the same thread under two host identities.

The workaround I used was:

  1. Back up the local and remote Codex databases and the affected rollout.
  2. Disable Remote Control auto-connect for the Linux machine.
  3. Stop the Linux Desktop/Remote Control process and the stale standalone app-server.
  4. Remove the Remote Control copy of the thread catalog/sync data while keeping the SSH copy.
  5. Restart Codex Desktop and reconnect through SSH.
  6. Open the thread using the SSH host identity.

After this, the catalog contained only the SSH mapping for the thread and the complete conversation loaded again.

Related issues:

  • #33750 - Allow SSH and Remote Control clients to share the same app-server
  • #22438 - Codex Desktop remote SSH connection does not show persisted remote sessions after app restart
  • #35733 - Desktop auto-connects discovered SSH hosts at startup and blocks local chat loading when a host is unavailable

This looks related to all three, but I am opening a separate report because I could confirm that one persisted thread was indexed under both the SSH and Remote Control host identities.

I can provide sanitized Desktop log excerpts, the relevant catalog rows, and the app-server read results. I will not attach the full rollout, state databases, credentials, SSH configuration, or unredacted host identifiers to a public issue.

View original on GitHub ↗

5 Comments

github-actions[bot] contributor · 11 days ago

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

  • #37789

Powered by Codex Action

MilkyWay008 · 10 days ago

That double-indexed thread_id under both remote-ssh-codex-managed:<ssh-alias> and remote-control:<environment-id> is the smoking gun...... the Desktop catalog is keyed on (host_id, thread_id) and you've got one thread, two host identities, both with projectId: null, so neither side owns it. Quickest unblock: pick ONE host identity for that thread, prune the duplicate entry from the local Desktop catalog, and force a re-rollout of the persisted thread under the surviving host. rm -rf the rollouts dir on the losing side is what fixed it for me before, but you already found that, so I'd check whether your recovery path is actually flipping the catalog and not just re-syncing the rollout file.

Vladx71 · 10 days ago

I it is interesting, some of the project discussions moving back
randomly to the appropriate projects. Maybe AI trying to figure out where
those conversations belongs :)

L:

On Mon, Aug 17, 2026 at 10:19 PM Ringo @.***> wrote:

MilkyWay008 left a comment (openai/codex#38927) <https://github.com/openai/codex/issues/38927#issuecomment-5319778110> That double-indexed thread_id under both remote-ssh-codex-managed:<ssh-alias> and remote-control:<environment-id> is the smoking gun...... the Desktop catalog is keyed on (host_id, thread_id) and you've got one thread, two host identities, both with projectId: null, so neither side owns it. Quickest unblock: pick ONE host identity for that thread, prune the duplicate entry from the local Desktop catalog, and force a re-rollout of the persisted thread under the surviving host. rm -rf the rollouts dir on the losing side is what fixed it for me before, but you already found that, so I'd check whether your recovery path is actually flipping the catalog and not just re-syncing the rollout file. — Reply to this email directly, view it on GitHub <https://github.com/openai/codex/issues/38927?email_source=notifications&email_token=AB3RO2TR7ZF52VSI5VCGB4L5KNSDLA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMZRHE3TOOBRGEYKM4TFMFZW63VGNVQW45LBNSSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#issuecomment-5319778110>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AB3RO2RLKSD7KNKENZX7ICD5KNSDLAVCNFSNUABFKJSXA33TNF2G64TZHM4TMNJUGE2TMNBZHNEXG43VMU5TKMJWG4ZDIMJQHA4KC5QC> . You are receiving this because you are subscribed to this thread.Message ID: @.***>
nos1609 · 1 day ago

A Windows reproduction confirms the same identity collision, this time between a local task and a Linux Remote SSH task rather than two remote connection methods.

Environment:

  • Codex Desktop 26.820.7780.0, Windows ARM64
  • Local CLI 0.149.1
  • One connected Linux Remote SSH host

Observed state before recovery:

  • The Desktop catalog contained the same thread_id under two host identities.
  • One row used host_id=local and a Windows workspace root.
  • The other row used a Remote SSH host identity and a Linux workspace root.
  • The local copy was current and contained activity from the same day.
  • The remote copy was stale and ended several weeks earlier.
  • Host-qualified read_thread calls returned different histories for the two objects.
  • list_threads collapsed the records by thread_id. A pinned stale remote record hid the newer local record from both the sidebar and pinned list.
  • navigate_to_codex_page accepts only threadId, not hostId, and opened the stale remote object.

The current local_thread_catalog schema explicitly uses the composite primary key:

PRIMARY KEY (host_id, thread_id)

Therefore duplicate IDs across hosts are valid catalog entries, but downstream list and navigation paths do not consistently preserve the host component.

A non-destructive recovery confirmed the boundary:

  1. Archive only the stale remote object by supplying both its exact threadId and Remote SSH hostId.
  2. Do not modify either rollout or the local catalog database.
  3. Refresh list_threads.
  4. The current local object immediately reappears as the first pinned task.
  5. Navigate by the same ID.
  6. Desktop now opens the current local history.

The archived-task listing still exposes the stale remote object with the same ID, while the active listing exposes the local object. No transcript data was lost.

Expected behavior:

  • Treat (host_id, thread_id) as the task identity throughout catalog hydration, pinning, deduplication, selection, and navigation.
  • Add an optional or required hostId to direct navigation.
  • If a hostless UI path must deduplicate, do not let an older stale object hide a newer object from another host.
  • Show an explicit host choice when the same ID resolves to multiple objects.

One diagnostic initially suggested a separate state-index issue because an older state_5.sqlite file did not contain the ID. I excluded that claim here: the file had not been updated for months, while the active Desktop catalog database was current. This report only relies on live host-qualified task APIs and the active catalog schema.

No task IDs, prompts, project names, usernames, hostnames, addresses, or private paths are included.

nos1609 · 1 day ago

Follow-up to the Windows reproduction above: archiving the stale Remote SSH object repaired host resolution and direct navigation, but it did not fully restore sidebar visibility.

Current read-only state:

  • list_threads returns the surviving local task under pinnedThreads;
  • it is active and has pinnedIndex: 1;
  • the active catalog contains the correct local row;
  • the global state contains the task in all relevant pin and project-order collections;
  • direct navigation opens the correct local history.

The actual Desktop sidebar still omits the task. Navigating to it from another task makes it usable, but navigating away leaves it undiscoverable again.

The trigger remains the cross-host duplicate described above. Desktop logs captured sidebar metadata requests trying to resolve a Windows cwd through the Remote SSH host before the stale object was archived. After archival, task routing became local, but the renderer did not rebuild its host-filtered sidebar projection.

Therefore the archive workaround is only partial:

  1. it prevents the stale remote object from winning hostless resolution;
  2. it does not guarantee sidebar rehydration from the surviving local task.

The remaining renderer/sidebar failure matches #33579. This suggests that a cross-host identity collision can leave stale client-side sidebar state even after the catalog ambiguity is removed.

No rollout, database, or persisted pin record was modified. Private identifiers, task content, project names, paths, addresses, and hostnames are omitted.