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 builds the TLS configuration and requires mTLS material to be complete. A client certificate without its key, or a key without its certificate, can never form a valid TLS key pair, so the combination is rejected with this error. Cert and key may come from either ClientCertPath/ClientKeyPath or the legacy CertPath/KeyPath aliases.
Source
Thrown at agents/drivers/etcd2-go/client.go:192
}
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
}
tlsConfig.Certificates = []tls.Certificate{pair}
}
return tlsConfig, nil
}
// probeClient verifies the endpoint speaks the v2 API. It returns the
// connected client and a probe result shaped like the v3 agent's:
// {ok, endpoint, limited?}.
func probeClient(endpoint string, connection connectionParams) (*authenticatedClient, map[string]any, error) {
httpClient, err := buildHTTPClient(connection)
if err != nil {
return nil, nil, errView on GitHub (pinned to c0390bff16)
Solutions
- Provide both certPath and keyPath (each resolves via firstNonBlank from Client*/Cert* and Client*/Key* aliases)
- If mTLS is not required, omit both paths so the plain-TLS path is used
- Verify both files exist and are readable at the given paths
Example fix
// before connection.ClientCertPath = "/etc/ssl/client.crt" // key missing // after connection.ClientCertPath = "/etc/ssl/client.crt" connection.ClientKeyPath = "/etc/ssl/client.key"
Defensive patterns
Strategy: validation
Validate before calling
certPath := firstNonBlank(conn.ClientCertPath, conn.CertPath)
keyPath := firstNonBlank(conn.ClientKeyPath, conn.KeyPath)
if (certPath == "") != (keyPath == "") {
return errors.New("mTLS config incomplete: provide BOTH client cert and key paths")
}
if certPath != "" {
if _, err := os.Stat(certPath); err != nil { return err }
if _, err := os.Stat(keyPath); err != nil { return err }
} Type guard
func mtlsComplete(c *Connection) bool {
cert := firstNonBlank(c.ClientCertPath, c.CertPath)
key := firstNonBlank(c.ClientKeyPath, c.KeyPath)
return (cert == "") == (key == "")
} Try / catch
client, err := buildHTTPClient(conn)
if err != nil {
if strings.Contains(err.Error(), "provided together") {
return fmt.Errorf("mTLS misconfiguration: set both client cert and key paths")
}
return err
} Prevention
- Treat cert+key as one atomic config unit; set both in the same change
- Validate mTLS completeness in config loading, before any connection attempt
- Check both files are mounted (secret volumes often mount only one)
- Cover with a test: cert-without-key and key-without-cert must both fail fast
When it happens
Trigger: Setting connection.ClientCertPath (or CertPath) without ClientKeyPath (or KeyPath), or vice versa, when building the HTTP client for an etcd v2 connection.
Common situations: Partial mTLS config in a YAML/JSON file where one path was edited or deleted; secret mounts where only one file was mounted; copying cert path but forgetting the key path in env vars.
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/3f0adfc7e5831358.
Report an issue: GitHub.