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

  1. Verify credentials manually: 'ssh -i <key> <user>@<host>' from the same machine Terraform runs on.
  2. Ensure the corresponding public key is in the target's ~/.ssh/authorized_keys.
  3. Double-check user, host, and port in the connection block; fix host_key after a rebuild.
  4. 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

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

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/3daba2b89362e265. Report an issue: GitHub.