hashicorp/terraform · error
cannot load client certificate: %w
Error message
cannot load client certificate: %w
What it means
tls.X509KeyPair could not build a certificate from the supplied client_certificate_pem + client_private_key_pem pair. The '%w' wraps the crypto/tls error, commonly 'tls: failed to find any PEM data in certificate input', 'tls: failed to find certificate PEM data', or a key/cert mismatch. Fires at Configure time, after the pair-presence check.
Source
Thrown at internal/backend/remote-state/http/backend.go:301
var tlsConfig tls.Config
client.HTTPClient.Transport.(*http.Transport).TLSClientConfig = &tlsConfig
if skipCertVerification {
// ignores TLS verification
tlsConfig.InsecureSkipVerify = true
}
if clientCACertificatePem != "" {
// trust servers based on a CA
tlsConfig.RootCAs = x509.NewCertPool()
if !tlsConfig.RootCAs.AppendCertsFromPEM([]byte(clientCACertificatePem)) {
return errors.New("failed to append certs")
}
}
if clientCertificatePem != "" && clientPrivateKeyPem != "" {
// attach a client certificate to the TLS handshake (aka mTLS)
certificate, err := tls.X509KeyPair([]byte(clientCertificatePem), []byte(clientPrivateKeyPem))
if err != nil {
return fmt.Errorf("cannot load client certificate: %w", err)
}
tlsConfig.Certificates = []tls.Certificate{certificate}
}
return nil
}
func (b *Backend) StateMgr(name string) (statemgr.Full, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
if name != backend.DefaultStateName {
return nil, diags.Append(backend.ErrWorkspacesNotSupported)
}
sm := &remote.State{Client: b.client}
if err := sm.RefreshState(); err != nil {
return nil, diags.Append(err)View on GitHub (pinned to c9def3e214)
Solutions
- Verify both files contain PEM armor: lines beginning with -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- (and the corresponding PRIVATE KEY----- markers).
- Re-derive from source with `openssl x509 -in client.crt -inform PEM -noout` (exit 0 = valid PEM cert) and `openssl pkey -in client.key -inform PEM -noout`.
- Confirm the key matches the cert: `openssl x509 -in client.crt -noout -modulus | openssl md5` vs the equivalent for the key (RSA), or use `openssl x509 -noout -pubkey`/`openssl pkey -pubout` comparison for general keys.
- If loading from a secret store, decode any base64 wrapping exactly once.
Example fix
// before (DER cert fed as PEM)
client_certificate_pem = file("client.der")
// after
client_certificate_pem = file("client.pem")
// generate with: openssl x509 -in client.der -inform DER -out client.pem -outform PEM Defensive patterns
Strategy: validation
Validate before calling
import (
"crypto/tls"
"fmt"
)
func validateKeyPair(certPEM, keyPEM []byte) error {
if _, err := tls.X509KeyPair(certPEM, keyPEM); err != nil {
return fmt.Errorf("client cert/key invalid: %w", err)
}
return nil
} Prevention
- Run `openssl x509 -in cert -noout` and `openssl pkey -in key -noout` in CI before deploy.
- Store PEM (not DER) in secrets; decode base64 exactly once.
- Add a pre-terraform step that calls tls.X509KeyPair on the configured values.
When it happens
Trigger: Either PEM value is not actually PEM (base64 of a DER blob, a JSON file, a header comment, truncated output); the key and cert do not correspond; wrong key type (e.g. an EC cert with an RSA key); a stray newline or BOM in the file content.
Common situations: file() reads a path that contains the cert in DER not PEM; secret was base64-encoded twice by the vault shim; copy-paste dropped the BEGIN/END markers; cert/key rotated and out of sync.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- client_certificate_pem is set but client_private_key_pem is
- client_private_key_pem is set but client_certificate_pem is
- must not be null
- must not be a whole number
- failed to parse unlock_address URL: %s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/2113f5e7dc6f6df5.
Report an issue: GitHub.