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

  1. Provide both params.ClientCertPath and params.ClientKeyPath together.
  2. If mutual TLS is not intended, clear both values so neither is set.
  3. 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

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

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/acd4da600c82ccf7. Report an issue: GitHub.