claude-code - 💡(How to fix) Fix [BUG] Workbench connection: "RPC transport closed during capability handshake" on Ubuntu — claude-ssh --serve daemon killed by systemd-logind (KillUserProcesses=yes) [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#54198Fetched 2026-04-29 06:33:40
View on GitHub
Comments
0
Participants
1
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
labeled ×3

Root Cause

claude-ssh --serve uses traditional Unix daemonization (forksetsidfork → close fds). This escapes the controlling terminal but not the systemd session cgroup the SSH server placed it in.

With KillUserProcesses=yes (the upstream logind.conf default — see man logind.conf), when the SSH session that ran --serve exits, systemd-logind tears down the session scope and SIGKILLs every process in the cgroup, including the daemonized RPC server. The next SSH session running --bridge then dials a vanished/orphaned socket — hence the alternating no such file or directory and connection refused errors.

Linger=yes does not help — it keeps [email protected] alive but does not exempt session-scoped processes from KillUserProcesses.

setsid -f does not help — it changes the controlling terminal but not the cgroup.

Fix Action

Fix / Workaround

Workaround verified to fix it

Code Example

[BinaryDeployment] Remote server started
[BinaryDeployment] Bridge channel opened
[BinaryDeployment] Bridge stderr: claude-ssh: dial server:
                   dial unix /home/<user>/.claude/remote/rpc.sock:
                   connect: no such file or directory
[RemoteRPCClient] Stream closed
[RemoteServerController] server.capabilities handshake failed,
                         assuming legacy server: Connection closed
[RemoteServerController] Connection failed: RPC transport closed during capability handshake

---

Claude remote server listening on /home/<user>/.claude/remote/rpc.sock
[Server] New connection from: @
[Server] Connection closed: @

---

'~/.claude/remote/server' '--stop'  '--socket' '~/.claude/remote/rpc.sock' \
&& '~/.claude/remote/server' '--serve' '--socket' '~/.claude/remote/rpc.sock' '--token-file' '~/.claude/remote/token.<hex>'

'~/.claude/remote/server' '--bridge' '--socket' '~/.claude/remote/rpc.sock'

---

# Terminal A
ssh workbench '~/.claude/remote/server --serve \
  --socket /tmp/v.sock --token-file <(echo t)'
# Returns 0 immediately, session ends.

sleep 2

# Terminal B (or new SSH session)
ssh workbench '~/.claude/remote/server --bridge --socket /tmp/v.sock < /dev/null'
# → claude-ssh: dial server: dial unix /tmp/v.sock: connect: connection refused

---

sudo sed -i 's/^#\?KillUserProcesses=.*/KillUserProcesses=no/' /etc/systemd/logind.conf
sudo systemctl restart systemd-logind
RAW_BUFFERClick to expand / collapse

Bug: claude-ssh --serve daemon killed by systemd-logind on Linux when KillUserProcesses=yes (the systemd default)

Severity: Workbench/SSH-remote feature is broken out-of-the-box on stock Ubuntu and any other distro shipping the upstream systemd default.

Affects:

  • Claude desktop app 1.4758.0 on macOS 26.5 (build 25F5058e), arm64
  • Internal SDK 2.1.119
  • claude-ssh build hash: e9237552c61a71aae8c0e586311078ab3275a0f2 (built 2026-04-22T00:07:35Z)
  • Linux remote: Ubuntu (kernel 6.17), systemd-logind, KillUserProcesses=yes (upstream default), Linger=yes, reproduces on both Tailscale SSH and OpenSSH transports

Related issues (same family, not duplicates):

  • #53606 — claude remote-control workspace-trust prompt under systemd
  • #30447 — feature request for daemonizable --headless remote-control

User-visible symptom

Connection failed: RPC transport closed during capability handshake when connecting Claude desktop to a remote Linux workbench over SSH. Auto-reconnect retries 3× and gives up. Reproduces every connect attempt.

Diagnosis

Captured from ~/Library/Logs/Claude/ssh.log:

[BinaryDeployment] Remote server started
[BinaryDeployment] Bridge channel opened
[BinaryDeployment] Bridge stderr: claude-ssh: dial server:
                   dial unix /home/<user>/.claude/remote/rpc.sock:
                   connect: no such file or directory
[RemoteRPCClient] Stream closed
[RemoteServerController] server.capabilities handshake failed,
                         assuming legacy server: Connection closed
[RemoteServerController] Connection failed: RPC transport closed during capability handshake

Captured server-side at ~/.claude/remote/remote-server.log:

Claude remote server listening on /home/<user>/.claude/remote/rpc.sock
[Server] New connection from: @
[Server] Connection closed: @

Exact invocations the desktop sends over SSH (from system journal):

'~/.claude/remote/server' '--stop'  '--socket' '~/.claude/remote/rpc.sock' \
&& '~/.claude/remote/server' '--serve' '--socket' '~/.claude/remote/rpc.sock' '--token-file' '~/.claude/remote/token.<hex>'

'~/.claude/remote/server' '--bridge' '--socket' '~/.claude/remote/rpc.sock'

Root cause

claude-ssh --serve uses traditional Unix daemonization (forksetsidfork → close fds). This escapes the controlling terminal but not the systemd session cgroup the SSH server placed it in.

With KillUserProcesses=yes (the upstream logind.conf default — see man logind.conf), when the SSH session that ran --serve exits, systemd-logind tears down the session scope and SIGKILLs every process in the cgroup, including the daemonized RPC server. The next SSH session running --bridge then dials a vanished/orphaned socket — hence the alternating no such file or directory and connection refused errors.

Linger=yes does not help — it keeps [email protected] alive but does not exempt session-scoped processes from KillUserProcesses.

setsid -f does not help — it changes the controlling terminal but not the cgroup.

Reproducer (no Claude.app required)

On a stock Ubuntu host with the default KillUserProcesses=yes:

# Terminal A
ssh workbench '~/.claude/remote/server --serve \
  --socket /tmp/v.sock --token-file <(echo t)'
# Returns 0 immediately, session ends.

sleep 2

# Terminal B (or new SSH session)
ssh workbench '~/.claude/remote/server --bridge --socket /tmp/v.sock < /dev/null'
# → claude-ssh: dial server: dial unix /tmp/v.sock: connect: connection refused

Workaround verified to fix it

sudo sed -i 's/^#\?KillUserProcesses=.*/KillUserProcesses=no/' /etc/systemd/logind.conf
sudo systemctl restart systemd-logind

After this, --serve daemons survive session end on any SSH transport (Tailscale SSH or OpenSSH), and Claude.app's workbench connection succeeds on its first attempt. Verified end-to-end: --bridge from a fresh session hits the listener, full handshake completes.

Suggested fixes (any one would resolve it for everyone)

  1. Run the daemon under [email protected] instead of the session scope. On Linux, when --serve is invoked, exec via systemd-run --user --scope --quiet -- <self> --serve … (or --unit=… / --collect) so the listener lives in [email protected] and is unaffected by session teardown. Fall back to current behavior when systemd-run is absent or --user bus is unavailable.
  2. Make --serve foreground-friendly and have the parent SSH session keep it alive. I.e., move the lifetime to the bridge, not the daemon: first --bridge connect spawns the server inline if the socket is missing, sharing the bridge's session scope. Subsequent bridges re-use it. (This is what VS Code Remote-SSH does.)
  3. Document the requirement. If neither of the above is acceptable, the workbench setup docs should call out KillUserProcesses=no (or KillExcludeUsers=<user>) as a prerequisite. Right now the failure mode is a generic "RPC transport closed" with no actionable guidance.

(1) is the right fix — it's a one-call change at the daemonization site, doesn't require user config, and matches how every other modern Linux daemon-ish thing handles this.

Files / artifacts

  • ~/Library/Logs/Claude/ssh.log — full handshake failure log
  • ~/Library/Logs/Claude/main.log — Claude desktop session log around the failures
  • ~/.claude/remote/remote-server.log (on workbench) — server-side log showing the lone @ connect-then-close pattern
  • system journal on workbench: journalctl -t tailscaled --since "10 min ago" shows the exact SSH command lines, useful for future repro

Happy to share any of these or run additional diagnostics.

extent analysis

TL;DR

The most likely fix is to run the claude-ssh daemon under [email protected] instead of the session scope to prevent it from being killed by systemd-logind when the SSH session ends.

Guidance

  • Identify the current systemd configuration by checking the value of KillUserProcesses in /etc/systemd/logind.conf.
  • Consider running the claude-ssh daemon under [email protected] using systemd-run --user --scope --quiet -- <self> --serve … to prevent it from being affected by session teardown.
  • Alternatively, modify the claude-ssh invocation to keep the daemon alive by running it in the foreground and having the parent SSH session manage its lifetime.
  • Verify the fix by checking the claude-ssh daemon's behavior after applying the suggested changes.

Example

systemd-run --user --scope --quiet -- claude-ssh --serve --socket /tmp/v.sock --token-file <(echo t)

This command runs the claude-ssh daemon under [email protected], which should prevent it from being killed by systemd-logind.

Notes

The suggested fix assumes that the systemd-run command is available on the system. If it's not, an alternative approach may be needed.

Recommendation

Apply workaround (1) by running the claude-ssh daemon under [email protected] using systemd-run to prevent it from being killed by systemd-logind. This approach is a one-call change and doesn't require user configuration, making it a more robust solution.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix [BUG] Workbench connection: "RPC transport closed during capability handshake" on Ubuntu — claude-ssh --serve daemon killed by systemd-logind (KillUserProcesses=yes) [1 participants]