hashicorp/terraform · error
client_private_key_pem is set but client_certificate_pem is…
Error message
client_private_key_pem is set but client_certificate_pem is not
What it means
Inverse of error 248: client_private_key_pem (or TF_HTTP_CLIENT_PRIVATE_KEY_PEM) is non-empty while client_certificate_pem (or TF_HTTP_CLIENT_CERTIFICATE_PEM) is empty. A private key alone cannot complete a TLS client-auth handshake; the certificate is required.
Solutions
- Set client_certificate_pem to the PEM certificate matching the private key.
- If mTLS is not required, remove client_private_key_pem entirely.
- Ensure TF_HTTP_CLIENT_CERTIFICATE_PEM and TF_HTTP_CLIENT_PRIVATE_KEY_PEM are both exported.
Example fix
// before
client_private_key_pem = file("client.key")
// client_certificate_pem missing
// after
client_certificate_pem = file("client.crt")
client_private_key_pem = file("client.key") Defensive patterns
Strategy: validation
Validate before calling
# Pre-flight: key and cert must both be set (or both unset)
cert="${TF_HTTP_CLIENT_CERTIFICATE_PEM:-}"
key="${TF_HTTP_CLIENT_PRIVATE_KEY_PEM:-}"
if [ -n "$key" ] && [ -z "$cert" ]; then
echo "ERROR: client_private_key_pem set but client_certificate_pem is empty"; exit 1
fi Prevention
- Always configure client_certificate_pem and client_private_key_pem as a pair.
- If mTLS is not needed, leave both unset.
- In CI, assert both TF_HTTP_CLIENT_*_PEM vars are exported together.
When it happens
Trigger: Private key supplied but the matching certificate was omitted.
Common situations: Certificate stored separately and not referenced; cert line dropped during templating; only the key was migrated to a new config.
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…
- cannot load client certificate
- invalid retry_max
- invalid retry_wait_max
- invalid retry_wait_min
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/10d1f35e1d640069.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/http/backend.go:279
configVal, "client_ca_certificate_pem",
"TF_HTTP_CLIENT_CA_CERTIFICATE_PEM", cty.StringVal(""),
).AsString()
clientCertificatePem := backendbase.GetAttrEnvDefaultFallback(
configVal, "client_certificate_pem",
"TF_HTTP_CLIENT_CERTIFICATE_PEM", cty.StringVal(""),
).AsString()
clientPrivateKeyPem := backendbase.GetAttrEnvDefaultFallback(
configVal, "client_private_key_pem",
"TF_HTTP_CLIENT_PRIVATE_KEY_PEM", cty.StringVal(""),
).AsString()
if !skipCertVerification && clientCACertificatePem == "" && clientCertificatePem == "" && clientPrivateKeyPem == "" {
return nil
}
if clientCertificatePem != "" && clientPrivateKeyPem == "" {
return fmt.Errorf("client_certificate_pem is set but client_private_key_pem is not")
}
if clientPrivateKeyPem != "" && clientCertificatePem == "" {
return fmt.Errorf("client_private_key_pem is set but client_certificate_pem is not")
}
// TLS configuration is needed; create an object and configure it
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 != "" {View on GitHub (pinned to d32a084675)