t8y2/dbx · error
Client certificate and key must be provided together
Error message
Client certificate and key must be provided together
What it means
tlsConfigFor validates mTLS material before building the client. A client certificate and its private key are only meaningful as a pair, so supplying exactly one of cert/key is rejected up front with this error rather than failing later inside tls.LoadX509KeyPair. It protects users from half-configured TLS setups.
Source
Thrown at agents/drivers/etcd-go/client.go:289
}
func tlsConfigFor(connection connectionParams) (*tls.Config, error) {
tlsConfig := &tls.Config{}
if ca := strings.TrimSpace(connection.CACertPath); ca != "" {
authorityPEM, err := os.ReadFile(ca)
if err != nil {
return nil, err
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(authorityPEM) {
return nil, fmt.Errorf("failed to parse CA certificate at %s", ca)
}
tlsConfig.RootCAs = pool
}
certPath := firstNonBlank(connection.ClientCertPath, connection.CertPath)
keyPath := firstNonBlank(connection.ClientKeyPath, connection.KeyPath)
if (certPath == "") != (keyPath == "") {
return nil, errors.New("Client certificate and key must be provided together")
}
if certPath != "" {
pair, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, err
}
if len(pair.Certificate) > 0 {
pair.Leaf, err = x509.ParseCertificate(pair.Certificate[0])
if err != nil {
return nil, err
}
}
tlsConfig.Certificates = []tls.Certificate{pair}
}
return tlsConfig, nil
}
func clientCertificateUsername(config *tls.Config) string {View on GitHub (pinned to c0390bff16)
Solutions
- Set both the client cert path and the client key path in the connection config.
- If only a CA is intended (no mTLS), remove the lone cert/key field and keep just the CA config.
- Verify secret/file mounts so both PEM files actually exist at the configured paths.
- Re-run the connection after fixing; buildClient will then load the pair via tls.LoadX509KeyPair.
Example fix
// before connection.ClientCertPath = "/certs/client.pem" // key missing client, err := buildClient(connection) // error // after connection.ClientCertPath = "/certs/client.pem" connection.ClientKeyPath = "/certs/client-key.pem" client, err := buildClient(connection)
Defensive patterns
Strategy: validation
Validate before calling
cert := firstNonBlank(conn.ClientCertPath, conn.CertPath)
key := firstNonBlank(conn.ClientKeyPath, conn.KeyPath)
if (cert == "") != (key == "") {
return errors.New("client cert and key must both be set (or both omitted)")
}
if cert != "" {
if _, err := os.Stat(cert); err != nil { return err }
if _, err := os.Stat(key); err != nil { return err }
} Type guard
func hasPairedTLSMaterial(conn connectionParams) bool {
hasCert := firstNonBlank(conn.ClientCertPath, conn.CertPath) != ""
hasKey := firstNonBlank(conn.ClientKeyPath, conn.KeyPath) != ""
return hasCert == hasKey
} Try / catch
client, err := buildClient(conn)
if err != nil {
if strings.Contains(err.Error(), "must be provided together") {
return nil, fmt.Errorf("TLS config incomplete: set both client cert and key paths: %w", err)
}
return nil, err
} Prevention
- Set cert and key paths together in config templates; never edit one alone.
- Startup-check that both PEM files exist and are readable before connect.
- Verify secret mounts contain both files (cert + key) in one unit.
- Keep a single config field pair (clientCertPath/clientKeyPath) rather than legacy aliases.
When it happens
Trigger: Setting connection.ClientCertPath (or CertPath) without ClientKeyPath (or KeyPath), or vice versa, when building the etcd client via buildClient.
Common situations: Partial TLS config in env/files — e.g. only ETCD_CLIENT_CERT_PATH exported; copy-paste config where the key line was dropped; rotating certs and updating only one path; secret mounts where one file failed to mount.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Client certificate and key must be provided together
- both client_cert_path and client_key_path are required for I
- failed to parse CA certificate at %s
- TDengine Rust WebSocket connector does not support client ce
- Client certificate and key must be provided together
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/0bb038bb3cce6fea.
Report an issue: GitHub.