{"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/c9def3e214014c1188faabfc4a5bde5095139765/internal/communicator/ssh/communicator.go#L193-L229","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconnection {\n  type        = \"ssh\"\n  user        = \"ubuntu\"\n  private_key = file(\"~/.ssh/wrong_key\")\n  host        = aws_instance.web.public_ip\n}\nError: SSH authentication failed (ubuntu@1.2.3.4:22): ssh: handshake failed: ssh: unable to authenticate\n\n// after\nconnection {\n  type        = \"ssh\"\n  user        = \"ubuntu\"\n  private_key = file(\"~/.ssh/id_ed25519\")\n  host        = aws_instance.web.public_ip\n}","handlingStrategy":"retry","validationCode":"// Sanity-check credentials before provisioning: try a one-shot SSH dial.\nfunc canSSHAuth(user, host string, port int, keyPath string) error {\n    key, _ := os.ReadFile(keyPath)\n    signer, err := ssh.ParsePrivateKey(key)\n    if err != nil {\n        return fmt.Errorf(\"bad private key: %w\", err)\n    }\n    cfg := &ssh.ClientConfig{User: user, Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)}, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: 15 * time.Second}\n    c, err := ssh.Dial(\"tcp\", fmt.Sprintf(\"%s:%d\", host, port), cfg)\n    if err != nil {\n        return err\n    }\n    c.Close()\n    return nil\n}","typeGuard":"null","tryCatchPattern":"// SSH auth errors are retryable by design; wrap in communicator.Retry.\nerr := communicator.Retry(ctx, func() error { return c.Connect(o) })\nif err != nil && strings.Contains(err.Error(), \"SSH authentication failed\") {\n    // credentials/host_key issue; do NOT retry forever — surface to user\n}","preventionTips":["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."],"tags":["terraform","provisioner","ssh","authentication","connection"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T20:17:04.800Z"}