t8y2/dbx · error
both client certificate and client key are required
Error message
both client certificate and client key are required
What it means
The driver supports mutual TLS: if either a client certificate or a client key is configured, both must be supplied, because a TLS client cert pair is meaningless (and would fail the handshake) without its matching private key. The configurer validates this pair before constructing the certificate provider.
Source
Thrown at agents/drivers/neo4j-go/driver.go:89
var tlsConfig *tls.Config
if params.CACertPath != "" {
certificate, err := os.ReadFile(params.CACertPath)
if err != nil {
return nil, fmt.Errorf("read Neo4j CA certificate: %w", err)
}
roots, err := x509.SystemCertPool()
if err != nil || roots == nil {
roots = x509.NewCertPool()
}
if !roots.AppendCertsFromPEM(certificate) {
return nil, errors.New("Neo4j CA certificate contains no valid PEM certificate")
}
tlsConfig = &tls.Config{MinVersion: tls.VersionTLS12, RootCAs: roots}
}
var clientCertificateProvider neo4jauth.ClientCertificateProvider
if params.ClientCertPath != "" || params.ClientKeyPath != "" {
if params.ClientCertPath == "" || params.ClientKeyPath == "" {
return nil, errors.New("both client certificate and client key are required")
}
provider, err := neo4jauth.NewStaticClientCertificateProvider(neo4jauth.ClientCertificate{
CertFile: params.ClientCertPath,
KeyFile: params.ClientKeyPath,
})
if err != nil {
return nil, fmt.Errorf("load Neo4j client certificate: %w", err)
}
clientCertificateProvider = provider
}
if tlsConfig == nil && clientCertificateProvider == nil {
return nil, nil
}
return func(driverConfig *config.Config) {
if tlsConfig != nil {
driverConfig.TlsConfig = tlsConfig
}
if clientCertificateProvider != nil {View on GitHub (pinned to c0390bff16)
Solutions
- Provide both params.ClientCertPath and params.ClientKeyPath together.
- If mutual TLS is not intended, clear both values so neither is set.
- Verify both files exist and are readable; the cert and key must be a matching pair.
Example fix
// before params.ClientCertPath = "/certs/client.pem" // key path missing // after params.ClientCertPath = "/certs/client.pem" params.ClientKeyPath = "/certs/client-key.pem"
Defensive patterns
Strategy: validation
Validate before calling
if (certPath == "") != (keyPath == "") {
return errors.New("client TLS requires both cert and key paths")
}
for _, p := range []string{certPath, keyPath} {
if p != "" {
if _, err := os.Stat(p); err != nil {
return fmt.Errorf("TLS file missing: %s", p)
}
}
} Prevention
- Set cert and key config values together, never one alone
- Verify mounted secret files exist in containers at startup
- Use a config struct whose constructor requires both fields
- Document which env vars form the mutual-TLS pair
When it happens
Trigger: Calling openDriver with params.ClientCertPath set but params.ClientKeyPath empty, or vice versa, when targeting a Neo4j instance requiring client certificates.
Common situations: Mounting only the cert into a container while the key remains a secret elsewhere; setting CLIENT_CERT_PATH but forgetting CLIENT_KEY_PATH; typo'd env var name for the key path.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Hive two-way TLS requires sslKeyStore or a client certificat
- load Neo4j client certificate: %w
- Hive two-way TLS requires sslTrustStore or a CA certificate
- Client certificate and key must be provided together
- Client certificate and key must be provided together
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/acd4da600c82ccf7.
Report an issue: GitHub.