t8y2/dbx · error

read Neo4j CA certificate: %w

Error message

read Neo4j CA certificate: %w

What it means

neo4jTLSConfigurer (called from openDriver) reads the CA certificate file at params.CACertPath to build the TLS trust pool. If os.ReadFile fails — missing file, wrong path, or permission denied — the driver wraps the OS error as 'read Neo4j CA certificate: ...'.

Source

Thrown at agents/drivers/neo4j-go/driver.go:75

		driverConfig.MaxConnectionLifetime = time.Hour
		driverConfig.TelemetryDisabled = true
	}}
	tlsConfigurer, err := neo4jTLSConfigurer(params)
	if err != nil {
		return nil, err
	}
	if tlsConfigurer != nil {
		configurers = append(configurers, tlsConfigurer)
	}
	return neo4j.NewDriver(uri, authToken, configurers...)
}

func neo4jTLSConfigurer(params connectParams) (func(*config.Config), error) {
	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,

View on GitHub (pinned to c0390bff16)

Solutions

  1. Verify the path exists and is readable: os.Stat / ls -l the ca_cert_path value
  2. Use an absolute path instead of a relative one, or set the correct working directory
  3. Fix file permissions so the driver process user can read the cert
  4. In containers, ensure the cert is mounted/injected before the driver starts
  5. Clear CACertPath if TLS with a custom CA is not actually needed (falls back to system roots)

Example fix

// before
params.CACertPath = "certs/neo4j-ca.pem" // relative, wrong cwd
// after
if _, err := os.Stat("/etc/neo4j/certs/neo4j-ca.pem"); err != nil {
    log.Fatal(err)
}
params.CACertPath = "/etc/neo4j/certs/neo4j-ca.pem"
Defensive patterns

Strategy: validation

Validate before calling

// Go: validate the CA cert path before connecting
func validateCACert(path string) error {
    if path == "" {
        return nil
    }
    info, err := os.Stat(path)
    if err != nil {
        return fmt.Errorf("CA cert %s: %w", path, err)
    }
    if info.IsDir() {
        return fmt.Errorf("CA cert %s is a directory", path)
    }
    f, err := os.Open(path)
    if err != nil {
        return fmt.Errorf("CA cert %s unreadable: %w", path, err)
    }
    return f.Close()
}

Try / catch

cfg, err := neo4jTLSConfigurer(params)
if err != nil {
    if strings.Contains(err.Error(), "read Neo4j CA certificate") {
        return nil, fmt.Errorf("fix ca_cert_path %q: %w", params.CACertPath, err)
    }
    return nil, err
}

Prevention

When it happens

Trigger: Connecting with a ca_cert_path/connectParams.CACertPath pointing to a file that does not exist, is unreadable by the process user, is a directory, or whose mount is unavailable.

Common situations: Relative path resolved from a different working directory than expected; container image missing the mounted cert; secrets file not yet injected at startup; wrong file permissions after k8s secret mount; typo'd path in the connection config.

Understand the failure class

Related errors


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