JuliusBrussee/caveman · error

postgres: set only or , not both

Error message

postgres: set only %s or %s, not both

What it means

caPEMFromEnvironment reads the CA either from a file path env var or from a direct PEM env var. Setting both is ambiguous, so the library rejects the configuration outright rather than guessing precedence.

Solutions

  1. Unset one of the two environment variables — keep either the file path or the inline PEM, not both.
  2. Standardize on the file variant (mount the CA as a file) in containerized deployments and remove the inline var from manifests.
  3. Audit the deployment environment (compose file, k8s env, shell profile) for both variable names.

Example fix

// before
POSTGRES_CA_FILE=/certs/ca.crt
POSTGRES_CA="-----BEGIN CERTIFICATE-----..."
// after
POSTGRES_CA_FILE=/certs/ca.crt
# POSTGRES_CA removed
Defensive patterns

Strategy: validation

Validate before calling

func caEnvConflict() bool {
	file := os.Getenv("POSTGRES_CA_FILE")
	direct := os.Getenv("POSTGRES_CA")
	return strings.TrimSpace(file) != "" && strings.TrimSpace(direct) != ""
}

Try / catch

if err := checkCAEnvSanity(); err != nil {
	log.Fatalf("CA config: %v", err)
}

Prevention

When it happens

Trigger: Calling ParsePoolConfig/NewPool while both the CA-file environment variable and the direct-PEM CA environment variable are set to non-empty values.

Common situations: A leftover CA path in the environment combined with an inline PEM injected by a secret manager; docker-compose or Helm chart exporting both variables; CI config accreting both settings over time.

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.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/4c9cfe2a70cd01a6. Report an issue: GitHub.

Appendix: source

Thrown at shared/platform/postgresconfig/postgresconfig.go:83

	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
}

func caPEMFromEnvironment() (string, error) {
	filePath := strings.TrimSpace(os.Getenv(caFileEnvironment))
	direct := strings.TrimSpace(os.Getenv(caEnvironment))
	if filePath != "" && direct != "" {
		return "", fmt.Errorf("postgres: set only %s or %s, not both", caFileEnvironment, caEnvironment)
	}
	if filePath == "" {
		return direct, nil
	}
	info, err := os.Stat(filePath)
	if err != nil {
		return "", fmt.Errorf("postgres: %s: %w", caFileEnvironment, err)
	}
	if !info.Mode().IsRegular() {
		return "", fmt.Errorf("postgres: %s must point to a regular file", caFileEnvironment)
	}
	contents, err := os.ReadFile(filePath)
	if err != nil {
		return "", fmt.Errorf("postgres: read %s: %w", caFileEnvironment, err)
	}
	return strings.TrimSpace(string(contents)), nil
}

View on GitHub (pinned to 3ee70a1026)