SSH sessions: history blank and fork fails due to tail connecting via raw IP instead of SSH config alias
Summary
When using Claude Code over SSH in the desktop app, two related features break after restarting the app:
- Session history vanishes — previously active sessions appear blank on the sidebar
- Forking SSH sessions fails — cannot fork/branch a conversation that was held over SSH
Both issues share the same root cause.
Root Cause
The desktop app runs two separate SSH operations when opening a previous SSH session:
- Byte-sync — downloads the session transcript (
.jsonl) from the remote server. Uses the SSH config hostname alias (e.g.dash), soIdentityFileand other SSH config settings are applied correctly. Works. - Tail — a persistent connection to stream live updates. Resolves the hostname to its raw IP (e.g.
root@62.238.23.200) and connects directly, bypassing the SSH config entirely. This meansIdentityFile,User, and other per-host settings are not applied. Always fails.
Log evidence (repeating every ~30–60 seconds):
[warn] [SSH] Failed to tail remote transcript: root@62.238.23.200: Permission denied (publickey,password).
Because the tail connection never succeeds, the app does not render existing history (even though the byte-synced transcript data is present in the local session JSON under completedTurns). The same tail failure also blocks session forking.
Steps to Reproduce
- Set up an SSH remote in Claude desktop app using an SSH config alias (e.g.
Host dashwithHostName <ip>andIdentityFile ~/.ssh/id_ed25519) - Start a Claude Code session over SSH and have a conversation
- Restart the Claude desktop app
- Open the previous SSH session from the sidebar → history is blank
- Attempt to fork the session → fails
Expected Behavior
- Session history should display from the locally cached
completedTurnsdata even before the tail connection is established - The tail connection should use the SSH config alias (not the resolved raw IP) so that
IdentityFileand other settings apply - Forking SSH sessions should work
Workaround
Add the raw IP as an additional Host alias in ~/.ssh/config so SSH config rules apply to both hostname and direct IP connections:
Host dash 62.238.23.200
HostName 62.238.23.200
User root
IdentityFile ~/.ssh/id_ed25519
AddKeysToAgent yes
UseKeychain yes
Then run ssh-add --apple-use-keychain ~/.ssh/id_ed25519 to load the key into the macOS Keychain.
Environment
- Platform: macOS (darwin 25.5.0)
- SSH config: hostname alias with
IdentityFile(passphrase-protected key) - SSH agent: empty on shell launch (key not pre-loaded)
Additional Notes
The session metadata (stored locally in ~/Library/Application Support/Claude/claude-code-sessions/) correctly records sshRemoteTranscriptPath pointing to the remote .jsonl file, and completedTurns is populated after a successful byte-sync. The data is there — the display/fork failure is a UI-layer dependency on the tail connection that should be decoupled.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗