JuliusBrussee/caveman · error
postgres: contains no valid certificate
Error message
postgres: %s contains no valid certificate
What it means
ParsePoolConfig validates the CA certificate PEM (taken from the CA env var or file) by appending it to an x509 cert pool. If AppendCertsFromPEM fails, the supplied data contains no parseable certificate, so the library refuses to build a hardened TLS pool. This prevents silently starting a pool without the intended CA trust anchor.
Solutions
- Ensure the CA env var contains full PEM text including -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- lines, or use the CA file env var pointing to a regular .pem/.crt file.
- Verify the file contents with `openssl x509 -in ca.crt -text -noout` to confirm it is a valid X.509 certificate.
- If you do not need custom TLS CA verification, unset the CA environment variable entirely so the pool uses system roots.
- Check for truncation or encoding corruption (e.g. YAML/base64 wrapping, CRLF mangling) when injecting the PEM via secret managers.
Example fix
// before export POSTGRES_CA="-----BEGIN PUBLIC KEY-----..." // not a certificate // after export POSTGRES_CA_FILE=/etc/ssl/certs/ca.crt // or POSTGRES_CA=$(cat ca.crt)
Defensive patterns
Strategy: validation
Validate before calling
func validPEM(pemStr string) bool {
return strings.Contains(pemStr, "-----BEGIN CERTIFICATE-----") &&
x509.NewCertPool().AppendCertsFromPEM([]byte(pemStr))
} Try / catch
config, err := postgresconfig.ParsePoolConfig(databaseURL)
if err != nil {
var pe *os.PathError
if errors.As(err, &pe) { /* env var misconfigured */ }
return fmt.Errorf("pool config: %w", err)
} Prevention
- Store CA certs as files and use the file-path env var instead of inline PEM.
- Validate PEM content at deploy time with openssl x509.
- Add a startup smoke test that calls ParsePoolConfig against the real environment.
When it happens
Trigger: Calling ParsePoolConfig (directly or via NewPool) with the CA environment variable set to a non-empty value that x509.CertPool.AppendCertsFromPEM cannot parse — e.g. garbage text, a raw public key, a private key, truncated PEM, or DER/binary bytes.
Common situations: CA env var set to the certificate path instead of PEM contents (the file variant should be used instead); pasting only part of a certificate; copying a base64 body without BEGIN/END lines; pointing the env var at a key file rather than a cert.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- ca_bundle
- ca bundle
- postgres: CA certificate configured while TLS is disabled
- postgres: must point to a regular file
- postgres: production connection includes a plaintext…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/76a86e2e707ec2ea.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/postgresconfig/postgresconfig.go:57
}
config, err := pgxpool.ParseConfig(databaseURL)
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
}View on GitHub (pinned to 3ee70a1026)