Reading Connection Logs
A ZestSSH connection log is an ordered record of everything the app did to reach your server, from DNS to shell. Because it runs top to bottom through fixed stages, the last line that succeeded tells you where the problem is. Most of diagnosing a failed connection is just reading down to the point where it stopped and then reading this page’s section for that stage.
When a connection fails, the error dialog has a Copy details button; the same button sits on the connection log viewer. Tap it to copy the whole structured log. Pasting that log verbatim is far more useful than describing the problem in words, and the support bot reads pasted logs directly. Do not trim it: the lines before the failure are what prove which stages worked.
Lines are prefixed with the stage that produced them, for example tcp_connect:, ssh_kex:, ssh_hostkey:, and ssh_auth:. Lines prefixed debug: come from the SSH engine itself and carry internal step names like SSHClient._authWithPublicKey.
The stages, in order
Section titled “The stages, in order”A successful connection walks these stages in order. Find the last one that produced a success line; the failure is in the stage after it.
Setup and identity: init:, identity:, shell:, connect:, Resolving
Section titled “Setup and identity: init:, identity:, shell:, connect:, Resolving”The opening lines prepare the connection and load your credentials, in this order:
init: Preparing connection to <host>:<port>— ZestSSH is starting the connection.identity: Loading credentials..., thenidentity: Public key loaded for <user>oridentity: Password credentials loaded for <user>— ZestSSH read the key or password from the selected identity.identity: Key is encrypted, prompting for passphrase...means the private key needed a passphrase.shell: Configuring terminal emulator— preparing the terminal.connect: Initiating connection..., thenResolving <host>...— opening the socket and a DNS lookup for the hostname.tunnel: Using pre-established tunnel to <host>:<port>— shown instead when the connection goes through a jump host.
Reaching these proves ZestSSH has your credentials in hand. Failing here is local: a missing key, a cancelled prompt (identity: User cancelled password prompt), or a hostname that will not resolve.
What the setup lines rule out
Section titled “What the setup lines rule out”The setup lines are not just noise; they eliminate causes before you even reach the failure:
identity: Public key loaded for <user>confirms the key exists and loaded from local storage. So if a later line shows an authentication failure, the problem is the server rejecting the key, not a missing or unreadable key on your device. It does NOT rule out the wrong key being selected, though: the client can successfully load a key the server has never seen, so cause 3 in the auth-failure list still stands.identity: Loading credentials...with no following... loaded ...line means the identity itself failed to load: a missing key (identity: ERROR - private key not found in storage), a wrong passphrase (identity: ERROR - wrong passphrase for encrypted key), or denied keychain access. That is a local problem, entirely different from a server-side rejection.- The
<user>inPublic key loaded for <user>is the account you are authenticating as. Confirm it matches the account that actually has your public key in~/.ssh/authorized_keyson the server: a wrong username produces an auth failure identical to a missing key.
Network path: tcp_connect: Connected
Section titled “Network path: tcp_connect: Connected”tcp_connect: Connected to <host>:<port> is the single most important line in the log.
If you see tcp_connect: Connected, the network is fine. Stop debugging firewalls, routing, port numbers, and VPNs. The TCP socket to the server’s SSH port is open. Everything after this line is SSH protocol and authentication, not networking. This one line eliminates the most common wasted troubleshooting path: re-checking firewall rules for what is actually an authentication problem.
If the log stops before this line, the problem is the network path — see “Fails before tcp_connect” below.
Protocol negotiation: ssh_session: Starting SSH-2.0
Section titled “Protocol negotiation: ssh_session: Starting SSH-2.0”ssh_session: Starting SSH-2.0 protocol negotiation, followed by debug: SSHTransport._processVersionExchange and debug: SSHTransport._remoteVersion = "...", means the server answered and identified itself as SSH. The quoted string is the server’s version banner, for example SSH-2.0-OpenSSH_9.6.
Reaching this proves you are talking to a real SSH service on that port. If tcp_connect succeeded but you never see _remoteVersion, you connected to something that is not SSH: a web server, a proxy, or the wrong port.
Algorithm negotiation: ssh_kex: Offering KEX, _kexType
Section titled “Algorithm negotiation: ssh_kex: Offering KEX, _kexType”Next the two sides agree on cryptography:
ssh_kex: Offering KEX: ...,ssh_cipher: Offering ciphers: ..., andssh_mac: Offering MACs: ...— the algorithms ZestSSH offered.debug: SSHTransport._handleMessageKexInit, thendebug: SSHTransport._kexType: <name>— the key exchange the two sides settled on. The negotiated cipher and MAC appear as_clientCipherTypeand_clientMacType.
Reaching a chosen _kexType proves the encrypted channel is up. Failing here means no algorithm is supported by both sides — see “KEX or cipher negotiation failure” below.
Host key verification: ssh_hostkey: Verifying host key
Section titled “Host key verification: ssh_hostkey: Verifying host key”ssh_hostkey: Verifying host key (<type>) then ssh_hostkey: Host key accepted means the server proved its identity and matched what ZestSSH has stored (trust on first use, or a previously accepted key).
ssh_hostkey: Host key REJECTED means the key did not match and you declined it. Reaching Host key accepted proves encryption and server identity are both good, so anything failing after this is authentication, not the server. A changed host key is security-critical — see “Host key mismatch” below.
Authentication: ssh_auth: Authenticating, _authWithPublicKey
Section titled “Authentication: ssh_auth: Authenticating, _authWithPublicKey”ssh_auth: Authenticating as <user> (<method>) starts auth, where <method> is publickey, password, or none. The debug: lines then trace each attempt:
debug: SSHClient._authWithPublicKey— offering a key.debug: SSHClient._authWithPassword— sending a password.debug: SSHClient._handleUserauthFailure— the server rejected that attempt.ssh_auth: SSH_AUTH_SUCCESS - authenticated— you are in.
If you reach auth and see _handleUserauthFailure with no success line, the server rejected your credentials — see “All authentication methods failed” below.
Session open: ssh_channel: Opening PTY
Section titled “Session open: ssh_channel: Opening PTY”After auth succeeds, ssh_channel: Opening PTY (xterm-256color) then ssh_session: Shell session established means the server allocated a shell and the session is live. A failure between SSH_AUTH_SUCCESS and the shell is rare, and usually a server-side restriction: a forced command, a disabled PTY, or a Match block denying the session.
All authentication methods failed
Section titled “All authentication methods failed”If your log reaches this, your connection is fine: the network, the encryption, and the host key all verified. The server simply rejected your key for that user. Lead with that; it reframes the whole problem, so stop debugging your side and look at the server.
This is the most common real failure, and its log shape is unambiguous:
tcp_connect: Connected to 10.50.0.100:22ssh_hostkey: Host key acceptedidentity: Public key loaded for deploydebug: SSHClient._authWithPublicKeydebug: SSHClient._handleUserauthFailuredebug: _currentAuthMethod = SSHAuthMethod.noneerror: Auth failed - Eub(All authentication methods failed)Notice identity: Public key loaded for deploy near the top: the key loaded, so a missing or unreadable local key is ruled out (but not the wrong key being selected), and it is not a network, firewall, or cipher problem either. The causes, ordered by how often they are to blame:
- The public key is not in
~/.ssh/authorized_keysfor that user on the server. By far the most common. Add the matching public key to that file. - Permissions are too open. SSH silently refuses keys unless
~/.sshis700and~/.ssh/authorized_keysis600, owned by that user. A group- or world-writable home directory fails the same way. - The wrong identity is selected in ZestSSH. The key attached to this connection does not match the one on the server. Check which identity the connection uses. A loaded key (
identity: Public key loaded) does NOT rule this out: the client can load a key the server has never seen. - The wrong username.
rootandadminare different accounts with differentauthorized_keysfiles. TheAuthenticating as <user>line shows which one you sent. - The server disallows it:
PubkeyAuthentication no, or anAllowUsers/AllowGroupsrule excluding the account, insshd_config.
To see the actual rejection reason, check the server’s auth log: /var/log/auth.log on Debian and Ubuntu, or journalctl -u sshd on systemd hosts. It records exactly why the key was refused.
Why PasswordAuthentication is not your fix here
Section titled “Why PasswordAuthentication is not your fix here”When the log shows debug: _currentAuthMethod = SSHAuthMethod.none after publickey, password authentication was never tried. That line is the client asking the server which methods it accepts. It is not a password attempt. A real password attempt logs debug: _currentAuthMethod = SSHAuthMethod.password and debug: SSHClient._authWithPassword. If you never see those, ZestSSH never sent a password, so changing PasswordAuthentication in sshd_config will not fix this. Fix the public-key causes above instead.
Other failure shapes
Section titled “Other failure shapes”Fails before tcp_connect
Section titled “Fails before tcp_connect”If the log stops at Resolving <host>... and never reaches tcp_connect: Connected, the problem is the network path, not SSH. Causes: the host is unreachable, the port is wrong (the default is 22), a firewall or security group is dropping the connection, the host is off your VPN or jump network, or DNS cannot resolve the name. Confirm the host and port, and that the box is reachable from this device.
KEX or cipher negotiation failure
Section titled “KEX or cipher negotiation failure”If you reach ssh_kex: Offering KEX but never get a _kexType, the client and server share no common key-exchange, cipher, or MAC. This happens with very old or very locked-down servers. Turn on Compatibility mode for the connection (it offers legacy algorithms: CBC ciphers and hmac-md5, shown as ssh_compat: Legacy algorithm support enabled), or update the server’s SSH configuration to allow a modern algorithm. Compare the offered ssh_cipher: and ssh_mac: lists against what the server permits.
Host key mismatch
Section titled “Host key mismatch”ssh_hostkey: Host key REJECTED, or a prompt warning that the host key changed, means the key the server presented does not match the one ZestSSH stored. This is the security-critical case: it can mean the server was rebuilt, or that traffic is being intercepted. Do not casually accept it. Confirm the new fingerprint out of band before trusting it — see the Host Key Verification page (host-key-verification.md).
Connects, then drops
Section titled “Connects, then drops”If the log reaches ssh_session: Shell session established and the session later dies, it is not a connection problem. Common causes: no keep-alive on an idle session behind a NAT or firewall that times out the TCP flow, or the mobile OS suspending the app in the background. Set a keep-alive interval on the connection, and on mobile keep the app in the foreground for long-running sessions.
Internal type tags: Eub(...), fsb(...)
Section titled “Internal type tags: Eub(...), fsb(...)”Release builds are obfuscated, so some log values carry a short class-name prefix in front of the real value:
Eub(All authentication methods failed)fsb([email protected])csb([email protected])gsb([email protected])
These prefixes are not error codes, and they change between builds — the same value may read yub( in one version and Eub( in another. Ignore the prefix. The value inside the parentheses is the real one: the algorithm name, or the error message. Read Eub(All authentication methods failed) as simply “All authentication methods failed.”