{"record":{"id":"3daba2b89362e265","repo":"hashicorp/terraform","slug":"ssh-authentication-failed-s-s-w","errorCode":null,"errorMessage":"SSH authentication failed (%s@%s): %w","messagePattern":"SSH authentication failed \\((.+?)@(.+?)\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/communicator/ssh/communicator.go","lineNumber":211,"sourceCode":"\tlog.Printf(\"[DEBUG] Connecting to %s for SSH\", hostAndPort)\n\tc.conn, err = c.config.connection()\n\tif err != nil {\n\t\t// Explicitly set this to the REAL nil. Connection() can return\n\t\t// a nil implementation of net.Conn which will make the\n\t\t// \"if c.conn == nil\" check fail above. Read here for more information\n\t\t// on this psychotic language feature:\n\t\t//\n\t\t// http://golang.org/doc/faq#nil_error\n\t\tc.conn = nil\n\n\t\tlog.Printf(\"[ERROR] connection error: %s\", err)\n\t\treturn err\n\t}\n\n\tlog.Printf(\"[DEBUG] Connection established. Handshaking for user %v\", c.connInfo.User)\n\tsshConn, sshChan, req, err := ssh.NewClientConn(c.conn, hostAndPort, c.config.config)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"SSH authentication failed (%s@%s): %w\", c.connInfo.User, hostAndPort, err)\n\n\t\t// While in theory this should be a fatal error, some hosts may start\n\t\t// the ssh service before it is properly configured, or before user\n\t\t// authentication data is available.\n\t\t// Log the error, and allow the provisioner to retry.\n\t\tlog.Printf(\"[WARN] %s\", err)\n\t\treturn err\n\t}\n\n\tc.client = ssh.NewClient(sshConn, sshChan, req)\n\n\tif c.config.sshAgent != nil {\n\t\tlog.Printf(\"[DEBUG] Telling SSH config to forward to agent\")\n\t\tif err := c.config.sshAgent.ForwardToAgent(c.client); err != nil {\n\t\t\treturn fatalError{err}\n\t\t}\n\n\t\tlog.Printf(\"[DEBUG] Setting up a session to request agent forwarding\")","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/communicator/ssh/communicator.go#L193-L229","documentation":"Wraps the underlying x/crypto/ssh NewClientConn failure during the SSH handshake for remote-exec/file provisioners. Despite the name 'authentication failed', it covers any handshake-time failure reported by the SSH library: bad credentials, wrong key, host key mismatch, unsupported auth method, or the server rejecting the user. Terraform logs it as WARN and retries within the connection timeout because hosts sometimes boot the sshd before auth data is ready.","triggerScenarios":"Wrong private_key or password for the target user; the user account does not exist on the remote; key is encrypted or in the wrong format; sshd is up but PAM/authorized_keys not yet populated (common on fresh cloud instances); algorithm/KEX mismatch with an old or restricted sshd.","commonSituations":"Using the default user on an AMI that requires a different one (e.g. 'ec2-user' vs 'ubuntu' vs 'admin'); pointing private_key at the wrong file; the instance's cloud-init has not finished injecting the public key; FIPS/hardened sshd that disabled the negotiated algorithms.","solutions":["Verify the user/key combination by SSHing manually from the same machine: ssh -i <key> <user>@<host>.","Confirm the correct username for the AMI/distribution (ubuntu for Ubuntu, ec2-user for Amazon Linux, admin for Debian, etc.).","Check the private_key file format (OpenSSH, not PuTTY PPK) and that it is unencrypted or provided via an agent.","Allow more startup time via connection.timeout, since sshd may precede auth readiness.","If host key verification is involved, set host_key appropriately or disable strict checking deliberately."],"exampleFix":"// before\nconnection {\n  user        = \"root\"\n  private_key = file(\"~/.ssh/id_rsa\")\n  host        = aws_instance.web.public_ip\n}\n\n// after\nconnection {\n  user        = \"ubuntu\"\n  private_key = file(\"~/.ssh/ubuntu-key\")\n  host        = aws_instance.web.public_ip\n}","handlingStrategy":"validation","validationCode":"# Smoke-test SSH before apply to catch auth issues early:\n#   $ ssh -i ~/.ssh/<key> -o StrictHostKeyChecking=no <user>@<host> 'echo ok'\n# In HCL, derive user/key from data sources rather than hardcoding:\n#   data \"aws_key_pair\" \"kp\" { ... } ; private_key = tls_private_key.kp.private_key_pem","typeGuard":null,"tryCatchPattern":"// In Go wrapping the provisioner, classify SSH auth failures as retryable\n// within a short window (host may still be booting), then fatal:\nif strings.Contains(err.Error(), \"SSH authentication failed\") {\n    if attempt < maxAttempts {\n        time.Sleep(backoff); continue\n    }\n    return fmt.Errorf(\"ssh auth failed after retries; check user/key: %w\", err)\n}","preventionTips":["Use the correct default user per AMI/distribution (ubuntu, ec2-user, admin).","Generate keys with tls_private_key and reference the PEM directly.","Smoke-test SSH manually before running apply on new images.","Allow enough startup time via connection.timeout."],"tags":["terraform","ssh","authentication","handshake","connection","provisioner"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}