hashicorp/terraform · error
cannot load client certificate
Error message
cannot load client certificate: %w
What it means
tls.X509KeyPair(clientCertificatePem, clientPrivateKeyPem) failed. Both PEM strings were supplied but the pair could not be assembled into a tls.Certificate. The '%w' wraps the underlying crypto/tls error. Causes: invalid PEM encoding, wrong PEM block type (e.g. 'CERTIFICATE REQUEST' instead of 'CERTIFICATE'), a key that does not match the certificate, corrupted/truncated PEM, or an unsupported key type.
Solutions
- Regenerate the cert/key pair and verify both load: openssl x509 -in client.crt -noout -text and openssl rsa -in client.key -check.
- Confirm the modulus matches: openssl x509 -in client.crt -modulus -noout | openssl md5 vs openssl rsa -in client.key -modulus | openssl md5.
- Ensure each value is full PEM including '-----BEGIN CERTIFICATE-----'/'-----BEGIN PRIVATE KEY-----' headers and footers.
- If reading via a secret manager, confirm the value is decoded (not base64) and uses LF line endings.
- Use file() to load from disk rather than inlining PEM in HCL to avoid escaping issues.
Example fix
// before (inline, often mangled)
client_certificate_pem = "-----BEGIN CERTIFICATE-----\nMIIB...==\n-----END CERTIFICATE-----"
client_private_key_pem = "-----BEGIN RSA PRIVATE KEY-----\nMIIE...==\n-----END RSA PRIVATE KEY-----"
// after (load full files from disk)
client_certificate_pem = file("${path.module}/client.crt")
client_private_key_pem = file("${path.module}/client.key") Defensive patterns
Strategy: validation
Validate before calling
# Pre-flight: verify the cert/key pair loads and the moduli match
openssl x509 -in client.crt -noout >/dev/null 2>&1 || { echo 'invalid cert PEM'; exit 1; }
openssl rsa -in client.key -check -noout >/dev/null 2>&1 || openssl pkey -in client.key -check -noout >/dev/null 2>&1 || { echo 'invalid key PEM'; exit 1; }
cmp -s <(openssl x509 -in client.crt -modulus -noout | openssl md5) <(openssl rsa -in client.key -modulus 2>/dev/null | openssl md5) \
|| cmp -s <(openssl x509 -in client.crt -pubkey -noout | openssl md5) <(openssl pkey -in client.key -pubout 2>/dev/null | openssl md5) \
|| { echo 'cert and key do not match'; exit 1; } Prevention
- Load PEM with file() from disk rather than inlining to avoid escaping/charset issues.
- Validate the cert/key pair with openssl before terraform init.
- Confirm secret-manager values are decoded (not base64) and use LF line endings.
- Regenerate the pair together and never mix certs and keys from different generations.
When it happens
Trigger: Cert and key are both present but are not a matching pair; one is not valid PEM; the 'private key' file actually contains a public key or CSR; PEM bytes were mangled (line-wrapping, charset, escaping) by a secret store or shell.
Common situations: Pasted cert/key truncated; copied the public cert into the key field; cert and key generated for different subjects; CRLF vs LF line endings introduced by Windows; secret manager base64-encoded the value and it was not decoded.
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…
- address must be HTTP or HTTPS
- Failed to make HTTP request
- failed to parse lock_address URL
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/2113f5e7dc6f6df5.
Report an issue: GitHub.
Appendix: 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 d32a084675)