Stuck? Start here
Follow the previous articles and it works first time eight or nine times out of ten. When it does not, it is almost always one of these eight. Find your error message and jump to it. Nobody needs to read this page from the top.
Connection timed out (Operation timed out)
Symptom
You run ssh user@host and the screen just sits there. After a long wait you get Operation timed out or Connection timed out, never having been offered a password prompt.
Causes
- A typo in the hostname, say
my-serverr. SSH reportsCould not resolve hostnamefor that straight away rather than hanging, so if you see that message go check the spelling — it is not what this section covers. What actually hangs is a wrong IP: a missing octet, one wrong digit, an address where nothing is listening. - SSH isn't on the standard port 22 on the other end and you left out
-p. - The target machine is asleep, or simply switched off.
- A firewall in between — home router, corporate network, hotel Wi-Fi — is dropping the connection, so the machine never receives your request.
Fix
Don't guess — confirm one hop at a time. First check whether the machine responds at all:
ping host-address
Nothing back from ping? Check the IP for typos, and check whether the machine is asleep or powered off. Some machines block ping on purpose, so a failure here does not prove the machine is unreachable — go straight to the next step.
Next, check whether anything is actually listening on the SSH port:
nc -vzw 5 host-address 22
Replace 22 with the port the other end really uses. The -w 5 caps the wait at five seconds, so a blocked port cannot leave the command hanging with nothing to show you.
Three outcomes. succeeded or open: the port is reachable, the problem is elsewhere. Connection refused: the machine received you, but nothing is listening there. Another timeout: something in between is dropping the packets, usually a firewall — look at the router configuration or the network you are on, not the server.
Permission denied (publickey)
Symptom
The connection is refused the instant you make it, printing Permission denied (publickey). without ever offering you a password prompt.
Cause
The server will not accept the key you are presenting. Most often the public key was never put on the server at all.
The other cause is easy to miss: permissions on ~/.ssh or authorized_keys that are too loose. SSH is strict here. Wrong permissions and it ignores the file completely, however correct the key inside is.
Fix
If you've never copied the public key across, the quickest route is ssh-copy-id: it logs in once with a password and appends your local public key to the server's authorized_keys.
ssh-copy-id user@host
That assumes password login is still enabled on the server. If it has already been turned off (see Password is correct but rejected below), you'll need someone with console access to that machine to paste the public key into ~/.ssh/authorized_keys directly.
If the key really is in there — one per line, complete, not truncated — and you are still refused, permissions are the likely culprit. Check them against the table. The last row is the one people miss: get ~/.ssh and authorized_keys both right, leave your home directory group- or world-writable, and StrictModes still refuses.
| Path | Required permissions | Command |
|---|---|---|
~/.ssh | 700 | chmod 700 ~/.ssh |
~/.ssh/authorized_keys | 600 | chmod 600 ~/.ssh/authorized_keys |
~ (the home directory itself) | Not writable by group or others | chmod go-w ~ |
Try connecting again once the permissions are fixed. If it still fails, add -v locally to watch the exchange and confirm SSH is really offering the key you think it is:
ssh -v user@host
That runs to fifty or sixty lines. You only need two of them. Offering public key: ... means your key really was sent, so nothing is wrong at your end. If Authentications that can continue: publickey follows it, the server received the key and declined it — wrong place or wrong permissions, so carry on with the steps above.
Password is correct but rejected
Symptom
You're certain the password is right — you may even have pasted it — but SSH keeps returning Permission denied, please try again., or ends without ever prompting you for one.
Cause
This usually has nothing to do with the password. The server's SSH configuration has password authentication switched off (PasswordAuthentication no), a common hardening choice that forces everyone onto keys and takes brute-force guessing off the table. With that set, a correct password gets refused a hundred times out of a hundred.
There is a trap here that costs more time than anything else on this page. Newer systems — Ubuntu after 22.10, Debian 12, nearly every cloud provider's default image — put a line near the top of sshd_config reading Include /etc/ssh/sshd_config.d/*.conf, which pulls in every config file in that directory. And sshd keeps the first value it reads for a setting. Later ones do not override it.
So if /etc/ssh/sshd_config.d/ holds something like 50-cloud-init.conf that also says PasswordAuthentication no, it wins over the line you just edited in the main file. You save, you restart, you are still refused.
Fix
The preferred fix is to switch to key authentication — see Permission denied (publickey) above for getting your public key into the server's authorized_keys.
Only change the configuration if you administer this server and have decided you do want password login. First find out whether another file will override the value you're about to edit:
sudo grep -r PasswordAuthentication /etc/ssh/sshd_config /etc/ssh/sshd_config.d/
If a file under /etc/ssh/sshd_config.d/ also sets PasswordAuthentication, change that value too (or comment the line out). Once that's clean, edit /etc/ssh/sshd_config and set PasswordAuthentication to yes.
Don't close your current connection yet. A broken sshd configuration can lock you out of the machine for good. Keep this window as your way back in until the new one is confirmed working. Check the syntax before anything else:
sudo sshd -t
Only once that prints no errors should you restart the service to apply it:
sudo systemctl restart ssh
On some distributions the service is called sshd rather than ssh. If the line above cannot find it, try sudo systemctl restart sshd. Then open a brand-new connection — not the one you kept as a fallback — and confirm password login works. Only then close the old window.
This whole setup runs on Tailscale, so there is a safer option than opening password login to the entire internet: allow passwords from the Tailscale range only, and require keys everywhere else. Add this at the end of sshd_config:
Match Address 100.64.0.0/10
PasswordAuthentication yes
Match All
PasswordAuthentication no
tmux not found
Symptom
Agentmux reports that it can't find tmux, or you connect yourself, type tmux, and get command not found: tmux.
Cause
tmux is not installed on the target machine yet, and that is not yours to handle. Agentmux installs it: it detects whether the machine uses apt, yum, dnf, pacman, apk (Alpine), or brew, then runs the matching command over SSH. Usually you wait for it to finish.
When that fails, the usual reason is sudo. The install command includes it, the channel is non-interactive, and sudo has no terminal to ask for a password on (see sudo: a terminal is required to read the password below). Second most common: Homebrew on a Mac while connected as root. Homebrew refuses to run as root by design.
A rarer case is easy to misread. tmux is installed, just somewhere Agentmux did not see when it probed the PATH — Linuxbrew under ~/.linuxbrew, MacPorts under /opt/local/bin. The message reads the same, but the problem is the PATH, not a missing install. Handle it as described in claude: command not found, but it runs fine in the desktop terminal below.
Fix
Start with the error Agentmux showed you. If it asks for a sudo password, or mentions passwordless sudo, type the password in and retry — that is usually faster than opening a separate window. If that fails too, or the message mentions a terminal or TTY, work through sudo: a terminal is required to read the password yourself. Once that is sorted the automatic install normally succeeds and you can stop there.
Install by hand only if it still won't install after the sudo problem is ruled out:
# Debian / Ubuntu
sudo apt install -y tmux
# RHEL / CentOS
sudo yum install -y tmux
# Fedora
sudo dnf install -y tmux
# Arch
sudo pacman -S tmux
# Alpine
sudo apk add tmux
# Mac (do not run as root)
brew install tmux
claude: command not found, but it runs fine in the desktop terminal
Symptom
You type claude in Terminal on the Mac and it runs. Connect through Agentmux, or run ssh host claude directly, and you get claude: command not found, or bash: claude: command not found. Same machine, same account, different result. It looks exactly like Agentmux being broken.
Cause
It is not Agentmux. It is how shells behave. This is the hardest one on the page to work out unaided, so the mechanism is worth the space — once you have it, you can diagnose anything that looks like this.
The Terminal you open on the Mac is an interactive login shell. claude is findable there because nvm, npm, or an installer added a line to some startup file putting its directory on the PATH. Which startup file differs between zsh and bash, and conflating them misleads, so here they are separately:
zsh (the default on macOS): an interactive login shell reads .zshenv → .zprofile → .zshrc → .zlogin, in that order. A connection that runs a single command, like ssh host claude, starts a non-interactive, non-login zsh that reads only .zshenv and skips everything else — including .zshrc, which is where most people's PATH setup lives.
bash: this case is stranger, and the behaviour varies by distribution rather than being uniform bash behaviour. An interactive login shell reads .bash_profile (or .profile), not .bashrc; the reason you think bash reads .bashrc too is that most distributions' default .bash_profile contains a snippet that sources .bashrc if it exists. As for whether bash reads ~/.bashrc when sshd invokes it to run a single command — the ssh host claude case — that depends on the distribution. It's a compile-time special case called SSH_SOURCE_BASHRC: Debian, Ubuntu, and their derivatives carry that patch, so even a non-interactive single-command shell reads ~/.bashrc. Distributions like RHEL, Fedora, and Arch ship upstream bash without it, and in the same situation ~/.bashrc is never read at all. Even on the ones that do read it, almost every default .bashrc opens with:
case $- in
*i*) ;;
*) return;;
esac
which means "if this isn't interactive, stop here and read no further." That check is usually right at the top of the file, so even when bash does open .bashrc, it bails out before reaching the PATH line you added — the same outcome as never reading it.
zsh or bash, the result is the same. The line that sets the PATH only ever ran when you opened Terminal yourself. SSH running a single command never read it, so the PATH has no entry for claude, and the shell is quite correctly telling you it cannot find it. Nothing to do with whether claude is installed. It was there the whole time. Nobody told this particular shell where to look.
None of it exists to make your life difficult, either. Skipping those files for non-interactive runs is deliberate: otherwise every remote command would first load a pile of interactive aliases and prompt configuration, and pick up that many more ways to go wrong.
One more detail. Agentmux probes the PATH on a Mac with zsh -lc — login, not interactive — which also skips .zshrc and reads only .zshenv and .zprofile. So for zsh users on a Mac, moving the PATH setup into those two files does more than fix your own manual SSH. It is what decides whether Agentmux can find the agent command at all.
Fix
Quickest option: find the absolute path to claude and call it directly instead of relying on the PATH. In the Terminal window where claude already works, run:
which claude
That prints something like /Users/yourname/.nvm/versions/node/v20.11.0/bin/claude. Put the whole path into the field where Agentmux asks for the agent command, in place of plain claude.
If you installed node with nvm, that path has the version number in it, so it breaks the next time you upgrade and you run which claude again. To stop redoing that, use the durable fix below.
The durable fix depends on your shell — don't mix the two:
On zsh, the macOS default: move the lines that set the PATH out of .zshrc and into ~/.zshenv. Usually those come from nvm, pyenv, or an export PATH=... you added yourself. ~/.zprofile works too — both are read by non-interactive SSH runs and by Agentmux when it probes.
On bash, moving them to ~/.profile or ~/.bash_profile will not help. ssh host claude starts a non-login shell and neither file is read.
On Debian, Ubuntu, or a derivative, put the PATH lines in ~/.bashrc above the case $- in ... esac block at the top, so they run before the interactive check bails out. On RHEL, Fedora, Arch and similar, ~/.bashrc is not read here at all and editing it changes nothing.
Not sure which camp your distribution is in? Use the absolute path. It works everywhere, and it is the only fix guaranteed to hold across distributions.
Once you've moved them, open a fresh SSH connection and confirm it took effect:
ssh host 'claude --version'
A version number means the PATH really is being read in a non-interactive shell this time.
Tailscale says connected, SSH still can't reach it
Symptom
The Tailscale app on your phone shows both devices green and online, but connecting with Agentmux or plain ssh still fails or times out entirely.
Causes
- You are connecting to a local network IP such as
192.168.x.x, rather than the MagicDNS name or the100.xTailscale IP. A local IP only works on that same Wi-Fi. The moment the phone switches to mobile data, the address means nothing. - The other device has just dropped off or gone to sleep, and the "online" state in the app hasn't caught up.
- Tailscale is working fine and the SSH service on the target machine was never enabled or started. Tailscale connects the two machines' networks. It does not turn SSH on for you.
- The most common real cause is an expired node key. The target machine needs to reauthenticate, and the app still shows a green "connected" while the machine is no longer on the network. Check the Tailscale admin console for a machine flagged as needing to sign in again. Reauthenticating usually settles it.
Fix
Tailscale on mobile has no command line, so tailscale status can only be run on the target machine — the Mac or Linux box you're connecting to:
tailscale status
On the target machine, confirm the phone's node shows as online. On the phone, open the Tailscale app and confirm the target machine looks healthy — not greyed out, no "needs to sign in again" warning.
If both look right, connect using the MagicDNS name (mac-mini.tail1234.ts.net, for example) or the 100.x IP from tailscale status. Never the local network IP. That way the address holds whether you are on home Wi-Fi or mobile data.
Once all of that checks out, go back to the target machine and confirm the SSH service is running — on Linux sudo systemctl status ssh, on a Mac see Remote Login is on but the Mac still won't accept connections below.
Remote Login is on but the Mac still won't accept connections
Symptom
Remote Login is enabled in System Settings and the Mac is definitely on the network, but SSH still can't connect — or it connects and is refused, and you're certain the password or key is correct.
Causes
- Remote Login defaults to allowing only the accounts in its "Only these users" list, and the account you are connecting as may not be on it.
- Once the Mac sleeps the network interface sleeps with it, so SSH has nothing to reach. Remote Login being on makes no difference. The machine is not answering network requests at all.
Fix
Open System Settings → General → Sharing. That is the path on macOS Ventura and later; before Ventura it is System Preferences → Sharing, with no "General" level. Find Remote Login, confirm the switch is on, then look inside at the list of users allowed access and check your account is there. Or select "All users".
Next open System Settings → Battery on a laptop, or System Settings → Energy Saver on a desktop that stays on as a server — a Mac mini, Mac Studio, or iMac, which is what mac-mini.tail1234.ts.net above would be. Increase or disable the idle sleep timer. If there is a wake-for-network option, usually labelled "Wake for network access", turn it on too. The Mac can then wake on an incoming connection and answer SSH from sleep.
sudo: a terminal is required to read the password
Symptom
Running a command containing sudo through Agentmux or SSH fails immediately with sudo: a terminal is required to read the password, showing no sign of having run at all.
Cause
Normally sudo prints a password prompt to the terminal and waits for you to type. When the command arrives over a non-interactive channel — SSH running a single remote command, or an install Agentmux runs in the background — there is no real terminal to show a prompt on and nowhere for you to type. Finding no TTY, sudo refuses outright rather than waiting forever for input that will never arrive.
A common misconception gets in the way here: that typing a sudo password just now means sudo will not ask again for a while. Modern sudo does record that you authenticated. But that record is tied to the TTY you were on (tty_tickets), not to your account and not to the machine.
So you type your password in an interactive SSH window, and moments later Agentmux runs a command over a fresh connection with no TTY. As far as sudo is concerned that is a different record, and you are asked again. The cache did not expire. It was never the same record being consulted.
Fix
Start with what Agentmux is telling you. When an automatic install fails it usually offers to take a sudo password, or suggests passwordless sudo, then retries. If you see that field, filling it in is often faster than handling this yourself.
If that channel is not available, SSH into the machine yourself, get a normal interactive session, and run the command by hand — sudo apt install -y tmux, say — entering the password and finishing the job.
That works, though not for the reason people assume. It unlocked nothing for Agentmux's later automated runs; as above, the two consult different TTY records. It works because you installed the thing yourself. The next non-interactive command finds the software already there, nothing to install, and the obstacle is gone.
If one specific command needs to run passwordless over non-interactive channels long-term, sudo visudo can add a NOPASSWD rule scoped to that command alone:
youraccount ALL=(ALL) NOPASSWD: /usr/bin/apt
That trade-off is worse than it looks. Granting NOPASSWD to a package manager like apt is very nearly granting full root. apt can be asked to install arbitrary packages, and hooks such as APT::Update::Pre-Invoke run arbitrary commands as root during the install. This is not a narrow exception. It is passwordless root, written a different way.
Configure it only if you understand that cost and are accepting it deliberately. Otherwise go back to installing once by hand. And never grant a blanket NOPASSWD: ALL — that drops the password requirement from sudo across the whole machine.