JuliusBrussee/caveman · error
postgres: CA certificate configured while TLS is disabled
Error message
postgres: CA certificate configured while TLS is disabled
What it means
ParsePoolConfig parsed a CA certificate (from the CA environment variable) and tried to install it as the TLS root pool, but the pgx connection config has TLSConfig == nil, meaning TLS is disabled for the connection. A CA is meaningless (and misleading) without TLS, so the library rejects the combination instead of ignoring the CA.
Solutions
- Set sslmode=verify-full in DATABASE_URL so pgx creates a TLSConfig to attach the CA to
- Remove the CA certificate environment variable if the connection is intentionally plaintext (local dev only)
- Check that pgxpool.ParseConfig did not strip TLS due to an earlier sslmode parsing issue in the URL
Example fix
// before DATABASE_URL="postgres://app:secret@db.internal:5432/prod?sslmode=disable" PG_CA=/etc/certs/ca.pem // after DATABASE_URL="postgres://app:secret@db.internal:5432/prod?sslmode=verify-full" PG_CA=/etc/certs/ca.pem
Defensive patterns
Strategy: validation
Validate before calling
if caPEMSet && !hasVerifyFull(databaseURL) {
return fmt.Errorf("CA certificate requires sslmode=verify-full (TLS enabled)")
} Try / catch
cfg, err := postgresconfig.ParsePoolConfig(databaseURL)
if err != nil && strings.Contains(err.Error(), "CA certificate configured while TLS is disabled") {
log.Fatal("sslmode=disable is incompatible with a configured CA; enable verify-full or drop the CA")
} Prevention
- Treat the CA secret and TLS-enabled sslmode as one unit in deployment templates
- Remove CA env vars from local/plaintext dev environments
- Document that the CA is only consumed when TLSConfig exists
- Smoke-test ParsePoolConfig with the exact production env in CI
When it happens
Trigger: Providing the CA certificate environment variable while the DATABASE_URL uses sslmode=disable or sslmode=allow/prefer in a way that leaves ConnConfig.TLSConfig nil; calling ParsePoolConfig with sslmode=disable plus a CA in non-production code paths that reach the CA handling block.
Common situations: Ops sets the CA env var in a deployment manifest but the DATABASE_URL still says sslmode=disable; local plaintext configs (TestLocalPlaintextRemainsAvailable) accidentally inheriting a CA secret from a shared environment.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- postgres: contains no valid certificate
- postgres: or is required in production
- ca_bundle
- ca bundle
- cave_gateway_identity_unverified
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/db7774ac3829be87.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/postgresconfig/postgresconfig.go:60
if err != nil {
return nil, fmt.Errorf("postgres: parse DATABASE_URL: %w", err)
}
caPEM, err := caPEMFromEnvironment()
if err != nil {
return nil, err
}
if production && caPEM == "" {
return nil, fmt.Errorf("postgres: %s or %s is required in production", caEnvironment, caFileEnvironment)
}
if caPEM == "" {
return config, nil
}
roots := x509.NewCertPool()
if !roots.AppendCertsFromPEM([]byte(caPEM)) {
return nil, fmt.Errorf("postgres: %s contains no valid certificate", caEnvironment)
}
if config.ConnConfig.TLSConfig == nil {
return nil, errors.New("postgres: CA certificate configured while TLS is disabled")
}
config.ConnConfig.TLSConfig.RootCAs = roots
config.ConnConfig.TLSConfig.InsecureSkipVerify = false
config.ConnConfig.TLSConfig.ServerName = config.ConnConfig.Host
for _, fallback := range config.ConnConfig.Fallbacks {
if fallback.TLSConfig == nil {
if production {
return nil, errors.New("postgres: production connection includes a plaintext fallback")
}
continue
}
fallback.TLSConfig.RootCAs = roots
fallback.TLSConfig.InsecureSkipVerify = false
fallback.TLSConfig.ServerName = fallback.Host
}
return config, nil
}
View on GitHub (pinned to 3ee70a1026)