hashicorp/terraform · error
SSH authentication failed (%s@%s): %w
Error message
SSH authentication failed (%s@%s): %w
What it means
In ssh Communicator.Connect at ssh/communicator.go:209, after the TCP connection succeeds, ssh.NewClientConn performs the SSH handshake and authentication. A non-nil error is wrapped at line 211 with the user and host:port. It is returned as a plain error (not a fatalError) so communicator.Retry can retry it — some hosts bring up sshd before auth is fully ready (see the comment at lines 213-216).
Source
Thrown at internal/communicator/ssh/communicator.go:211
log.Printf("[DEBUG] Connecting to %s for SSH", hostAndPort)
c.conn, err = c.config.connection()
if err != nil {
// Explicitly set this to the REAL nil. Connection() can return
// a nil implementation of net.Conn which will make the
// "if c.conn == nil" check fail above. Read here for more information
// on this psychotic language feature:
//
// http://golang.org/doc/faq#nil_error
c.conn = nil
log.Printf("[ERROR] connection error: %s", err)
return err
}
log.Printf("[DEBUG] Connection established. Handshaking for user %v", c.connInfo.User)
sshConn, sshChan, req, err := ssh.NewClientConn(c.conn, hostAndPort, c.config.config)
if err != nil {
err = fmt.Errorf("SSH authentication failed (%s@%s): %w", c.connInfo.User, hostAndPort, err)
// While in theory this should be a fatal error, some hosts may start
// the ssh service before it is properly configured, or before user
// authentication data is available.
// Log the error, and allow the provisioner to retry.
log.Printf("[WARN] %s", err)
return err
}
c.client = ssh.NewClient(sshConn, sshChan, req)
if c.config.sshAgent != nil {
log.Printf("[DEBUG] Telling SSH config to forward to agent")
if err := c.config.sshAgent.ForwardToAgent(c.client); err != nil {
return fatalError{err}
}
log.Printf("[DEBUG] Setting up a session to request agent forwarding")View on GitHub (pinned to c9def3e214)
Solutions
- Verify credentials manually: 'ssh -i <key> <user>@<host>' from the same machine Terraform runs on.
- Ensure the corresponding public key is in the target's ~/.ssh/authorized_keys.
- Double-check user, host, and port in the connection block; fix host_key after a rebuild.
- For freshly-booted hosts, rely on the built-in retry or raise the connection timeout.
Example fix
// before
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/wrong_key")
host = aws_instance.web.public_ip
}
Error: SSH authentication failed (ubuntu@1.2.3.4:22): ssh: handshake failed: ssh: unable to authenticate
// after
connection {
type = "ssh"
user = "ubuntu"
private_key = file("~/.ssh/id_ed25519")
host = aws_instance.web.public_ip
} Defensive patterns
Strategy: retry
Validate before calling
// Sanity-check credentials before provisioning: try a one-shot SSH dial.
func canSSHAuth(user, host string, port int, keyPath string) error {
key, _ := os.ReadFile(keyPath)
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
return fmt.Errorf("bad private key: %w", err)
}
cfg := &ssh.ClientConfig{User: user, Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)}, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: 15 * time.Second}
c, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", host, port), cfg)
if err != nil {
return err
}
c.Close()
return nil
} Type guard
null
Try / catch
// SSH auth errors are retryable by design; wrap in communicator.Retry.
err := communicator.Retry(ctx, func() error { return c.Connect(o) })
if err != nil && strings.Contains(err.Error(), "SSH authentication failed") {
// credentials/host_key issue; do NOT retry forever — surface to user
} Prevention
- Pre-validate the private key parses and the public key is authorized before Terraform runs.
- Pin host_key or rebuild-aware keys for ephemeral hosts to avoid mismatch failures.
- Confirm user/host/port correctness; test 'ssh -i key user@host' manually first.
When it happens
Trigger: ssh.NewClientConn returns non-nil during the handshake — wrong password, wrong/unauthorized private key, host-key mismatch, unsupported key-exchange algorithm, or sshd not yet ready.
Common situations: Wrong private_key/password in the connection block; public key not in the target's ~/.ssh/authorized_keys; host_key mismatch after a host rebuild; user typo; ephemeral cloud host where sshd is still initializing on first boot.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- connection type '%s' not supported
- ssh client is not connected
- Error creating new client connection via proxy: %s
- Error connecting to bastion: %s
- failed to parse private key %q: %s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/3daba2b89362e265.
Report an issue: GitHub.