hashicorp/terraform · error
failed to parse private key %q: %s
Error message
failed to parse private key %q: %s
What it means
Raised in signCertWithPrivateKey when ssh.ParseRawPrivateKey fails parsing the private key string used for certificate-based SSH authentication. This occurs when both private_key and certificate are configured (the cert-signer code path). The raw private key must be parseable by Go's x/crypto/ssh for the cert signer to be built.
Source
Thrown at internal/communicator/ssh/provisioner.go:400
if opts.password != "" {
conf.Auth = append(conf.Auth, ssh.Password(opts.password))
conf.Auth = append(conf.Auth, ssh.KeyboardInteractive(
PasswordKeyboardInteractive(opts.password)))
}
if opts.sshAgent != nil {
conf.Auth = append(conf.Auth, opts.sshAgent.Auth())
}
return conf, nil
}
// Create a Cert Signer and return ssh.AuthMethod
func signCertWithPrivateKey(pk string, certificate string) (ssh.AuthMethod, error) {
rawPk, err := ssh.ParseRawPrivateKey([]byte(pk))
if err != nil {
return nil, fmt.Errorf("failed to parse private key %q: %s", pk, err)
}
pcert, _, _, _, err := ssh.ParseAuthorizedKey([]byte(certificate))
if err != nil {
return nil, fmt.Errorf("failed to parse certificate %q: %s", certificate, err)
}
usigner, err := ssh.NewSignerFromKey(rawPk)
if err != nil {
return nil, fmt.Errorf("failed to create signer from raw private key %q: %s", rawPk, err)
}
ucertSigner, err := ssh.NewCertSigner(pcert.(*ssh.Certificate), usigner)
if err != nil {
return nil, fmt.Errorf("failed to create cert signer %q: %s", usigner, err)
}
return ssh.PublicKeys(ucertSigner), nilView on GitHub (pinned to c9def3e214)
Solutions
- Verify the private_key value is a complete, valid PEM private key (check with ssh-keygen -l -f keyfile).
- Ensure the key is not passphrase-encrypted; decrypt it first if it is (ssh-keygen -p).
- Confirm the key format is supported (RSA, ECDSA, Ed25519 in PEM/OpenSSH format).
- Check the file() path or variable interpolation that supplies the key content.
Example fix
// before
connection {
private_key = "-----BEGIN OPENSSH PRIVATE KEY-----\n...truncated..."
certificate = var.cert
}
// after
connection {
private_key = file("~/.ssh/id_ed25519")
certificate = file("~/.ssh/id_ed25519-cert.pub")
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the private key is parseable before constructing the communicator
func validatePrivateKey(pk string) error {
block, _ := pem.Decode([]byte(pk))
if block == nil {
return errors.New("private key is not valid PEM")
}
if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
return errors.New("private key is encrypted — decrypt it before use")
}
if _, err := ssh.ParseRawPrivateKey([]byte(pk)); err != nil {
return fmt.Errorf("failed to parse private key: %w", err)
}
return nil
} Prevention
- Use file() to read the key content to avoid truncation from copy-paste.
- Decrypt passphrase-protected keys with ssh-keygen -p before use.
- Use standard key types: RSA, ECDSA, or Ed25519 in PEM/OpenSSH format.
When it happens
Trigger: The private_key value is not valid PEM, uses an unsupported key format, is truncated/corrupted, or is passphrase-encrypted (ParseRawPrivateKey does not handle encrypted keys in this path).
Common situations: The private_key file content was read incorrectly (e.g. file() path wrong), the key is in a format Go's SSH library does not support, the key was copy-pasted with missing lines or trailing whitespace, or the key is OpenSSH encrypted.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to parse certificate %q: %s
- failed to create signer from raw private key %q: %s
- failed to create cert signer %q: %s
- SSH authentication failed (%s@%s): %w
- Error creating new client connection via proxy: %s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/58bd12f10113ee32.
Report an issue: GitHub.