[BUG] Desktop SSH connections fail when ~/.ssh is an iCloud Drive symlink — spawned ssh/ssh-keygen can't read known_hosts or config

Status Open
Reported on v2.1.237
Maintainer reply None cached
Activity 0 comments · opened Aug 22, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

The desktop app's "Add SSH connection" always fails with Host denied (verification failed) and:

"<host>" is not in ~/.ssh/known_hosts yet, or its host key has changed. If this is your first connection to it, run ssh to this host once in a terminal and accept the host key, then test again. If you have connected before and the machine was not reinstalled, do not connect.

Command-line ssh to the same host from the same Mac succeeds, and the host key is in ~/.ssh/known_hosts and current (verified by comparing fingerprints against /etc/ssh/ssh_host_*_key.pub read over an already-trusted session). Following the dialog's advice therefore changes nothing.

Entering an alias from ~/.ssh/config in the SSH Host field fails earlier, at getaddrinfo ENOTFOUND, even though the field's helper text offers "a host from ~/.ssh/config".

Root cause

On this machine ~/.ssh is a symlink into iCloud Drive (~/Library/Mobile Documents/com~apple~CloudDocs/...), a TCC-protected location, so one keystore can be shared between two Macs.

The app shells out to ssh -G and ssh-keygen -F. Those children get the correct HOME and resolve to /usr/bin/ssh and /usr/bin/ssh-keygen, but they do not inherit Claude.app's kTCCServiceFileProviderDomain grant, so ~/.ssh is effectively invisible to them. The app's own Node fs reads are unaffected, which is what makes this hard to diagnose.

Evidence (from ~/Library/Logs/Claude/ssh.log)

  1. identityFiles: 5 — real ssh -G <host> returns 1 identity file for this host. 5 is exactly what ssh -F none -G <host> returns, i.e. the user config was never read.
  2. An alias defined in ~/.ssh/config is logged as Host <alias> is not in known_hosts — the raw string was used as the hostname, so ssh -G output was discarded.
  3. The rejection is always the "lookup returned nothing" branch (host is not in known_hosts), never the "presented key does not match" branch. No key comparison ever happens.
  4. keys: 1[ssh-rsa] — an identity file read in-process from that same iCloud-backed directory (the agent holds 3 keys, so this is the file from the dialog, not the agent).

Host key type is not involved: adding every key type the server offers, in any order, changes nothing, because the file is never read.

Related but distinct: #72735 reports the same error string caused by a genuine host key rotation.

What Should Happen?

Two things:

  1. The connection should succeed. The host key is present and current in ~/.ssh/known_hosts, and command-line ssh accepts it. Reading known_hosts and ssh_config in-process — the way identity files are already read — would remove the dependency on TCC grants that spawned OpenSSH binaries do not inherit.
  1. The error should distinguish "the known_hosts lookup could not run" from "the presented key does not match." The current message asserts the host is unknown or re-keyed and warns "do not connect", when in fact nothing was compared. Only a genuine mismatch warrants the MITM warning; as written, it sends users off to re-record a host key that is already correct, which cannot help.

Error Messages/Logs

~/Library/Logs/Claude/ssh.log (host/user redacted):

[info]  [SSH2Connection] Resolved <user>@<host> -> <user>@<host>:22 (identityFiles: 5)
[warn]  [SSH2Connection] Host <host> is not in known_hosts
[info]  [SSH2Connection] Connecting to <user>@<host>:22 (agent: env:/var/run/com.apple.launchd.XXXXX/Listeners, keys: 1[ssh-rsa], proxy: false, keyboard: true)
[warn]  [SSH2Connection] Rejecting host key for <host> - host is not in known_hosts
[error] [SSH2Connection] Connection error: Host denied (verification failed)
[error] [RemoteServerController] Connection failed (166ms, trigger: send_message, attempt 1): Host denied (verification failed)
[warn]  [RemoteServerController] Auto-reconnect aborted: terminal error class "hostkey" (Host denied (verification failed))

With a ~/.ssh/config alias in the SSH Host field, the alias is never resolved:

[warn]  [SSH2Connection] Host <alias> is not in known_hosts

UI, when an alias is entered:

  Couldn't find the host "<alias>". Check the spelling in SSH Host, or connect to your VPN if it is an internal name.
  getaddrinfo ENOTFOUND <alias>

Same shell, same user, same HOME - the lookups the app relies on all succeed:

  $ ssh-keygen -F <host>
  # Host <host> found: line 51
  <host> ssh-ed25519 AAAAC3NzaC1lZDI1NTE5...

  $ ssh -G <host> | grep -c '^identityfile'
  1
  $ ssh -F none -G <host> | grep -c '^identityfile'
  5          # <- the number the app logs

  $ ssh <alias>
  <remote hostname>            # alias resolves fine via ~/.ssh/config

Steps to Reproduce

On macOS, with ~/.ssh relocated into iCloud Drive (a common setup for sharing one keystore between two Macs):

  1. Move ~/.ssh into iCloud Drive and symlink it back:

``
mv ~/.ssh "$HOME/Library/Mobile Documents/com~apple~CloudDocs/ssh-store"
ln -s "$HOME/Library/Mobile Documents/com~apple~CloudDocs/ssh-store" ~/.ssh
``

  1. Confirm OpenSSH is happy from a terminal - both of these must succeed:

``
ssh <user>@<host> # connects
ssh-keygen -F <host> # prints the recorded host key
``

  1. In the desktop app, add an SSH connection: SSH Host <user>@<host>, port empty, Identity File set to a key in ~/.ssh.
  2. Click Test connection.

Observed: Connection failed: Host key not trusted / Host denied (verification failed), with the dialog claiming the host is not in ~/.ssh/known_hosts or has been re-keyed.

  1. Open ~/Library/Logs/Claude/ssh.log. It shows Host <host> is not in known_hosts and identityFiles: 5 - the latter being the count you get only when ~/.ssh/config is not read (ssh -F none -G).

Also reproducible in step 3 by entering a ~/.ssh/config alias instead of <user>@<host>: it fails at getaddrinfo ENOTFOUND because the alias is never resolved through the config.

A machine with a normal, local ~/.ssh does not reproduce this.

Claude Model

None

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

2.1.237 (Claude Code), in Claude desktop 1.34493.1

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Environment

  • Claude desktop 1.34493.1 / Claude Code 2.1.237
  • macOS 26.5.2, Apple Silicon
  • OpenSSH from /usr/bin (both ssh and ssh-keygen resolve there on the login-shell PATH)

Ruled out while diagnosing

  • Missing or stale host key entries. All host key types the server offers were added, verified against /etc/ssh/ssh_host_*_key.pub over an already-trusted session, and reordered; no effect. The file is never read.
  • Malformed known_hosts. 63 lines, no CRs, no markers, no blank or short lines, only ssh-ed25519 / ecdsa-sha2-nistp256 / ssh-rsa.
  • Wrong HOME in spawned children. ps -Eww on a process the app spawned shows HOME=/Users/<user>.
  • ssh-keygen not on PATH, or a slow/failing login-shell PATH probe. It resolves to /usr/bin/ssh-keygen, and zsh -lc returns in ~25 ms.
  • TCC being denied to the app itself. Both com.anthropic.claude-code and com.anthropic.claudefordesktop hold kTCCServiceFileProviderDomain (allowed), and the app reads an identity file from the same directory in-process. Only the spawned OpenSSH binaries are affected.

Workarounds

None that keep the setup intact. Moving config and known_hosts back to real local files outside iCloud Drive would fix it, at the cost of the shared keystore. Granting Full Disk Access to /usr/bin/ssh and /usr/bin/ssh-keygen may also work, but that is a broad grant to system binaries and I did not test it.

Note: this reproduces on both Macs sharing the keystore, so the app cannot SSH out from either.

View original on GitHub ↗