gofr-dev/gofr · error

failed to load client certificate: %w

Error message

failed to load client certificate: %w

What it means

Raised in registerMySQLTLSConfig when DB_TLS_CLIENT_CERT and DB_TLS_CLIENT_KEY are both set but tls.LoadX509KeyPair fails to parse them as a matching client key pair. gofr uses this to configure mutual TLS (mTLS) for MySQL; a bad pair aborts TLS setup.

Source

Thrown at pkg/gofr/datasource/sql/sql.go:538

	caCertPool := x509.NewCertPool()
	if !caCertPool.AppendCertsFromPEM(caCert) {
		return errFailedCACerts
	}

	tlsConfig := &tls.Config{
		RootCAs:    caCertPool,
		ServerName: dbConfig.HostName,
		MinVersion: tls.VersionTLS12,
	}

	// Optional: Support client certificates (mutual TLS)
	clientCertPath := os.Getenv("DB_TLS_CLIENT_CERT")
	clientKeyPath := os.Getenv("DB_TLS_CLIENT_KEY")

	if clientCertPath != "" && clientKeyPath != "" {
		clientCert, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath)
		if err != nil {
			return fmt.Errorf("failed to load client certificate: %w", err)
		}

		tlsConfig.Certificates = []tls.Certificate{clientCert}

		logger.Debug("loaded client certificate for mutual TLS")
	}

	return mysql.RegisterTLSConfig("custom", tlsConfig)
}

func getServerName(hostname string) string {
	// For localhost/127.0.0.1, use "localhost" explicitly
	if hostname == "127.0.0.1" || hostname == "::1" {
		return localhost
	}

	return hostname
}

View on GitHub (pinned to 187eb24962)

Solutions

  1. Verify the cert and key match: compare modulus (openssl x509 -noout -modulus; openssl rsa -noout -modulus).
  2. Check both files exist, are readable, and are valid PEM (openssl x509 -in cert; openssl rsa -in key).
  3. Ensure the key is unencrypted or re-export without a passphrase.
  4. Fix DB_TLS_CLIENT_CERT / DB_TLS_CLIENT_KEY env values if swapped or mistyped.

Example fix

// before
DB_TLS_CLIENT_CERT=/certs/old-client.crt
DB_TLS_CLIENT_KEY=/certs/new-client.key  // mismatched pair
// after
DB_TLS_CLIENT_CERT=/certs/client.crt
DB_TLS_CLIENT_KEY=/certs/client.key  // matching pair
Defensive patterns

Strategy: validation

Validate before calling

certPEM, _ := os.ReadFile(os.Getenv("DB_TLS_CLIENT_CERT"))
keyPEM, _ := os.ReadFile(os.Getenv("DB_TLS_CLIENT_KEY"))
if _, err := tls.X509KeyPair(certPEM, keyPEM); err != nil {
    return fmt.Errorf("client cert/key invalid or mismatched: %w", err)
}

Prevention

When it happens

Trigger: NewSQL (mysql dialect) with both DB_TLS_CLIENT_CERT and DB_TLS_CLIENT_KEY set to files that don't exist, are malformed PEM, or where the cert and key don't match (different key pairs).

Common situations: Cert renewed but key not updated (or vice versa), swapped cert/key env values, encrypted private key, DER-format client cert, or missing files in the container.

Understand the failure class

Related errors


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/cf3fbcad2aaf61a1. Report an issue: GitHub.