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

  1. 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.
  2. Verify the file contents with `openssl x509 -in ca.crt -text -noout` to confirm it is a valid X.509 certificate.
  3. If you do not need custom TLS CA verification, unset the CA environment variable entirely so the pool uses system roots.
  4. 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

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.

Related errors


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)