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

  1. Verify both files contain PEM armor: lines beginning with -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- (and the corresponding PRIVATE KEY----- markers).
  2. 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`.
  3. 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.
  4. 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

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

Related errors


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