[BUG] Desktop shows "no messages yet" for SSH/remote sessions on reopen (transcript intact on remote host, context preserved)

Status Closed — duplicate
Reported on v2.1.202
Maintainer reply None cached
Activity 5 comments · opened Jul 9, 2026 · closed Aug 25, 2026

Summary

When Claude Code Desktop connects to a remote host over SSH, closing and reopening the app shows "no messages yet" for those remote sessions — the message list renders empty. However, the conversation context is fully intact: asking the model to "summarize this conversation" returns the entire prior discussion. The transcript .jsonl on the remote host is complete and uncorrupted.

So this is a client-side hydration bug specific to remote/SSH sessions: the Desktop UI on the Mac does not load/render the remote host's transcript message list on reopen, even though the remote backend reads it correctly (hence preserved context).

Local (Mac-directory) sessions are unaffected — their history displays normally on reopen.

Environment

  • Desktop app (macOS): Claude 1.19367.0 (1a5be1), 2026-07-07
  • Remote host: Raspberry Pi 5, Ubuntu 24.04.4 LTS, aarch64, Linux 6.8.0-1060-raspi
  • Remote backend (from session env): CLAUDE_CODE_ENTRYPOINT=claude-desktop, CLAUDE_CODE_EXECPATH=/home/<user>/.claude/remote/ccd-cli/2.1.202, CLAUDE_SSH_DAEMON_CHILD=1

Steps to reproduce

  1. From Claude Code Desktop (macOS), connect to a Linux host over SSH and hold a normal multi-turn conversation.
  2. Fully quit the Desktop app on the Mac and reopen it.
  3. Open the remote (SSH) conversation.

Expected

The full message history renders in the UI (as it does for local Mac-directory sessions).

Actual

  • The message list shows "no messages yet" (empty).
  • Context is not lost: "summarize this conversation" returns the complete prior discussion.
  • The transcript on the remote host is complete and well-formed.

Evidence (data is intact on the remote host)

The remote .jsonl for one such session holds the full history, yet the Desktop UI showed it as empty on reopen:

$ f=~/.claude/projects/-home-<user>-Documents-MAC/f213b833-....jsonl
$ grep -c '"type":"user"'      $f   -> 128
$ grep -c '"type":"assistant"' $f   -> 230
$ wc -l < $f                        -> 571
# roles are present and ordered from the start of the file (user, attachments, user, ...)

128 user + 230 assistant messages present on the remote host, but the Mac UI rendered "no messages yet" — while the backend clearly still reads the same file (context preserved). This rules out transcript corruption and points to the Desktop client not fetching/rendering the remote transcript on reopen.

Not a duplicate of #24304

#24304 ("Conversation history missing on resume (except last message)") is caused by JSONL corruption (colliding messageId/uuid, broken parent chain), affects local sessions too, and shows the last message. This report is different: the JSONL is uncorrupted, it is specific to remote/SSH sessions, local sessions are fine, and the UI shows zero messages ("no messages yet"), not the last one.

Likely same area as #76114

#76114 reports that Desktop remote (SSH) sessions never create file-history snapshots on the remote host, breaking rewind. Both look like the same underlying gap: the Desktop client fails to sync/hydrate remote-host session state back to the Mac UI — there it's the file-history snapshots, here it's the transcript message list.

View original on GitHub ↗

4 Comments

stephenc-git · 1 month ago

Corroborating report — same bug on ccd-cli 2.1.205, x86_64 Ubuntu remote, triggered by a host reboot + app relaunch. Different setup than the OP, which suggests this isn't macOS-specific and still reproduces (or regressed) in 2.1.205.

Environment

  • Remote host (SSH target): x86_64, Ubuntu 24.04.4 LTS, Linux 6.8.0-134-generic
  • Remote backend: CLAUDE_CODE_ENTRYPOINT=claude-desktop, exec path ~/.claude/remote/ccd-cli/2.1.205, active server build under ~/.claude/remote/srv/
  • Desktop client connects to this box over SSH.
  • Regression timeline: remote toolchain auto-updated 2.1.197 (Jul 5) → 2.1.202 (Jul 7) → 2.1.205 (Jul 10 23:28). Blank-on-switch appeared right afterward, following a host reboot + desktop relaunch. (OP was on 2.1.202; still present at 2.1.205.)

Same symptom

Switching to / reopening a remote SSH session shows "No messages yet" over a fully intact transcript. Verified on the remote host:

  • one session .jsonl: 547 user + 1315 assistant messages, wc -l = 2559, 0 malformed lines
  • another: 153 user + 341 assistant, wc -l = 655, 0 malformed lines

Context is preserved (the backend clearly reads the file) — only the UI message list renders empty.

Extra observations that may help localize it

  1. The server side is healthy — the resume succeeds; only client-side hydration fails. The remote server accepts the connection, spawns ccd-cli … --resume <sessionId> … --replay-user-messages --include-partial-messages, streams stdout/stderr, and every subprocess exits 0. remote-server.log has no errors. ps shows exactly one clean 2.1.205 --resume process per open tab (no orphans, no version mixing). This strongly supports the "client-side hydration bug specific to remote/SSH sessions" framing in the OP.
  2. Some tabs silently fork to a brand-new empty session. At least one desktop tab kept its title but was bound to a freshly-created, near-empty sessionId (new UUID containing only the post-reboot turns), while the original full transcript sat untouched under a different UUID. This looks like the same failure as #73526 (cliSessionId missing/nulled → a new message forks a disconnected session).
  3. ~/.claude.jsonprojects[<remote-project>].history is [] after the relaunch — consistent with #63025's "projects field nulled after desktop restart," though here it's specifically the history array that's empty.

Reliable workaround

SSH into the remote host and run claude --resume <id> from a terminal — the CLI hydrates the full history correctly. (Close the desktop tab for that session first, so two processes don't attach to one session.)

Likely the same root cluster as #63025, #50982, #73526, #53717.

ccchen077511 · 1 month ago

**Corroborating report with a root-cause clue: the session registry entry carries a different session ID than the on-disk transcript, so a lookup by registry ID finds nothing.**

Environment

  • Desktop client: Claude Code Desktop on macOS, remote-controlling a Windows PC over SSH
  • Remote host: WSL2 Ubuntu on that PC (Linux 6.18.33.2-microsoft-standard-WSL2, x86_64)
  • Remote CLI: 2.1.212, CLAUDE_CODE_ENTRYPOINT=claude-desktop
  • Model/session: claude-fable-5, cwd=/home/<user>/dev

Evidence of the ID mismatch

A remote session created from the desktop app shows an intact list entry but an empty message list. Querying the session registry (via the session-management MCP from a sibling session on the same remote host):

{
  "sessionId": "local_1aa7a0d1-35e7-4c91-9a4c-9c4527b3a495",
  "title": "PC: Dev directory sync check",
  "cwd": "/home/<user>/dev",
  "createdAt": "2026-07-16T14:14:44.245Z",
  "lastActivityAt": "2026-07-17T14:30:44.112Z",
  "isRemote": true
}

There is no transcript file for 1aa7a0d1… under ~/.claude/projects/ on the remote host. The actual transcript lives under a different UUID:

~/.claude/projects/-home-<user>-dev/1efe9748-a38f-4d12-b7ed-104db5b734ac.jsonl
  1,400 lines
  every entry's internal sessionId: 1efe9748-a38f-4d12-b7ed-104db5b734ac
  first event: 2026-07-16T14:14:46.074Z   (≈2 s after the registry entry's createdAt)
  last event:  2026-07-17T14:30:43.808Z   (matches the registry entry's lastActivityAt to the second)

The creation/last-activity timestamps line up to the second, so these are unambiguously the same session under two IDs: the registry (which the desktop UI and the session-management list_events API resolve by) holds one ID, while the transcript was written under another. list_events for the registry ID returns 0 of 0 messages; the UI shows the same emptiness.

This would explain the "context preserved but UI empty" behavior upthread: whatever resolves the live backend finds the real session, but anything that resolves through the registry ID → transcript-file path finds nothing.

Workaround

claude --resume 1efe9748-… on the remote host loads the full history into a continuation session, which then registers correctly and is visible from the desktop app afterwards. The original registry entry remains a dead pointer.

myspiritbook · 1 month ago

Confirming I'm hitting this too on macOS. Several sessions in the sidebar (with real, non-trivial diffs visible in the footer bar, e.g. "+22,816 -624" on a branch) open to a blank "No messages yet" pane instead of the transcript. It's reproducible and has persisted across multiple full app restarts. My workflow matches the description here: I have Claude Code running both locally on a Mac and via SSH from a second machine against the same repo, so this is very likely the remote/SSH-session hydration case described in the summary — local-only sessions for me do reload fine.

rmagur1203 · 25 days ago

I reproduced the same symptom and was able to identify and fix the cause in my environment.

This may not explain every report in this issue, but it appears to be a reproducible failure mode for SSH connections configured with password-only authentication.

Environment

  • Claude Desktop: 1.25927.0 (003700)
  • Build timestamp: 2026-08-04T05:34:55.000Z
  • Desktop OS: macOS
  • Remote host: Linux over SSH
  • Remote Claude Code CLI: 2.1.221
  • Remote entrypoint: claude-desktop
  • Initial SSH authentication: password only, without public-key authentication

When reopening an existing remote session, the session title was still visible, but the message pane displayed:

No messages yet

The backend process was launched with the original session ID:

~/.claude/remote/ccd-cli/2.1.221
  --output-format stream-json
  --verbose
  --input-format stream-json
  --resume=<session-id>
  --replay-user-messages
  --include-partial-messages
  ...

The previous context was also preserved. The backend could still understand and summarize the earlier conversation even though the Desktop UI displayed no messages.

Initial investigation

Because the remote transcript was intact while the UI was empty, I initially considered several possible causes:

  1. ccd-cli was reading the transcript for model context but not emitting historical messages as replay events.
  2. server --serve was dropping replay events before sending them through rpc.sock.
  3. server --bridge was not forwarding the events to Claude Desktop.
  4. Claude Desktop was receiving the events but failing to hydrate the message list.
  5. Claude Desktop could not locate or synchronize the remote JSONL transcript.
  6. The Desktop session registry was pointing to a different session ID than the actual transcript file.

The remote transcript was intact

The transcript existed under the expected project directory:

~/.claude/projects/<remote-project>/<session-id>.jsonl

The filename matched the session ID passed to ccd-cli --resume.

The JSONL file contained the previous user and assistant messages, and the internal sessionId values matched the filename.

For example:

{
  "type": "assistant",
  "sessionId": "<session-id>",
  "entrypoint": "claude-desktop",
  "timestamp": "2026-08-04T14:36:42.484Z"
}

This ruled out:

  • transcript deletion,
  • an empty transcript,
  • missing historical messages,
  • a mismatch between the resumed session ID and the transcript filename,
  • and loss of the backend conversation context.

The remote server and RPC connection were healthy

The expected remote processes were running:

server --serve
  --socket ~/.claude/remote/run/<run-id>/rpc.sock
  --token-file ~/.claude/remote/run/<run-id>/token.<token-id>

server --bridge
  --socket ~/.claude/remote/run/<run-id>/rpc.sock

ccd-cli/2.1.221
  --resume=<session-id>
  --replay-user-messages
  ...

The Unix socket was both listening and connected:

rpc.sock  LISTEN
rpc.sock  ESTAB

The process file descriptors showed the expected topology:

Claude Desktop / SSH transport
        |
        v
server --bridge
        |
        | Unix socket
        v
server --serve
        |
        | stdin/stdout/stderr pipes
        v
ccd-cli --resume=<session-id>

The control channel between server --serve and ccd-cli was also working.

For example, the server sent an MCP configuration request:

{
  "type": "control_request",
  "request": {
    "subtype": "mcp_set_servers"
  }
}

And ccd-cli returned a successful response:

{
  "type": "control_response",
  "response": {
    "subtype": "success"
  }
}

This ruled out a dead remote server, a broken Unix socket, or an unresponsive ccd-cli process.

strace confirmed that ccd-cli read the complete transcript

I traced the cold-resume path using strace.

The resumed process opened the correct transcript and read it completely:

read(
  16</home/<user>/.claude/projects/<remote-project>/<session-id>.jsonl>,
  ...
)

read(
  16</home/<user>/.claude/projects/<remote-project>/<session-id>.jsonl>,
  ...
)

read(
  16</home/<user>/.claude/projects/<remote-project>/<session-id>.jsonl>,
  "",
  16
) = 0

The final read() = 0 confirms that the file was read to EOF.

At that point, the state was:

Transcript exists
→ correct transcript is opened
→ transcript is read completely
→ backend context is restored
→ Desktop UI still shows "No messages yet"

This initially made it look as though the CLI was using the JSONL only to restore model context without replaying the historical messages to the Desktop UI.

It was not a symlink or transcript discovery issue

I also checked whether ~/.claude/projects or one of its parent paths was a symlink that the transcript discovery code might not follow.

Both normal find and symlink-following find -L returned the same transcript:

find "$HOME/.claude/projects" \
  -name '<session-id>.jsonl'

find -L "$HOME/.claude/projects" \
  -name '<session-id>.jsonl'

Both resolved to the same path:

/home/<user>/.claude/projects/<remote-project>/<session-id>.jsonl

Therefore, this was not caused by symlink traversal or transcript discovery failure.

The actual cause was visible in the macOS Desktop logs

The Claude Desktop logs showed that Desktop attempted to synchronize the remote transcript:

[SSH] Byte-syncing transcript for session <local-session-id> from remote

Immediately afterward, it repeatedly logged:

[SSH] Failed to tail remote transcript:
<user>@<host>: Permission denied (publickey,password).

This was the key clue.

The initial SSH connection used password authentication and worked because Claude Desktop displayed an interactive password prompt.

However, transcript synchronization and transcript tailing appear to use a separate background or non-interactive SSH process.

That process could not display a password prompt, so authentication failed.

The effective flow in my environment was:

Initial SSH connection
→ Claude Desktop displays a password prompt
→ password authentication succeeds
→ remote server and ccd-cli start normally

Existing session is reopened
→ Desktop starts a separate transcript byte-sync/tail SSH process
→ background SSH cannot prompt for a password
→ SSH authentication fails
→ remote JSONL is not synchronized into the Desktop message list
→ Desktop displays "No messages yet"

This explains why all of the following could be true at the same time:

  • the initial SSH connection worked,
  • ccd-cli --resume launched normally,
  • the remote JSONL was complete,
  • the backend read the JSONL to EOF,
  • the model retained the previous context,
  • but the Desktop message list remained empty.

The transcript was not missing from the remote host. The separate SSH connection responsible for syncing or tailing it could not authenticate.

Resolution

I configured public-key authentication for the remote host.

After installing the public key on the remote server, I verified that non-interactive SSH authentication succeeded:

ssh -o BatchMode=yes <user>@<host> true
echo $?

The expected result is:

0

No password prompt or other interactive authentication should be required.

I then fully quit Claude Desktop, reopened it, reconnected to the same remote host, and opened the same existing session.

The previous messages were displayed normally in the Desktop UI.

No transcript repair, session migration, JSONL modification, or session fork was required.

Conclusion

In my case, the root cause was:

Password-only SSH authentication
→ initial interactive SSH connection succeeds
→ background transcript synchronization uses another SSH process
→ non-interactive authentication fails
→ Desktop cannot hydrate the historical message list
→ "No messages yet"

Configuring public-key authentication resolved the issue immediately.

This is probably not the only possible cause of this symptom. Other comments describe separate failure modes, such as a mismatch between the session registry ID and the transcript ID.

However, password-only SSH authentication appears to be a reproducible cause that produces almost identical user-visible behavior.

Suggested improvements

1. Reuse the authenticated SSH connection

If possible, transcript byte-sync and tailing should reuse the SSH transport that Claude Desktop has already authenticated.

Currently, the initial password-authenticated connection can succeed while a separate non-interactive SSH process used for transcript synchronization fails. This leaves the remote backend fully functional but prevents the Desktop UI from loading the message history.

2. Surface the authentication failure in the UI

Currently, the UI only displays:

No messages yet

This makes the transcript appear empty or corrupted, even though the actual failure is SSH authentication during transcript synchronization.

A more accurate error would be something like:

Failed to load the remote transcript because
background SSH authentication failed.

Configure public-key authentication for this host
and reconnect.

Showing cached comments. Read the full discussion on GitHub ↗