JuliusBrussee/caveman · critical
postgres: parse DATABASE_URL
Error message
postgres: parse DATABASE_URL: %w
What it means
ParsePoolConfig validates the DATABASE_URL (including production sslmode requirements) and wraps any error from pgxpool.ParseConfig with this message. It means the URL could not be parsed as a valid PostgreSQL connection string by pgx — malformed syntax, unknown scheme, or invalid connection parameters. The pgx error is preserved via %w.
Solutions
- Print/inspect the pgx validation error wrapped inside this message — it names the exact offending parameter.
- URL-encode the password: use net/url to build the DSN (url.UserPassword) instead of string concatenation.
- Ensure the scheme is postgres:// or postgresql:// and the host/port/dbname are present.
- Trim quotes/whitespace and verify the env var is actually set in the deployment.
- Test the URL with psql or `pgx` directly to isolate config vs. library issues.
Example fix
// before
dsn := fmt.Sprintf("postgres://user:%s@db:5432/app", password) // password contains @/
// after
u := &url.URL{Scheme: "postgres", User: url.UserPassword("user", password), Host: "db:5432", Path: "/app"}
dsn := u.String() Defensive patterns
Strategy: validation
Validate before calling
func validateDatabaseURL(raw string) error {
if strings.TrimSpace(raw) == "" {
return errors.New("DATABASE_URL is empty")
}
u, err := url.Parse(raw)
if err != nil {
return fmt.Errorf("DATABASE_URL not parseable: %w", err)
}
if u.Scheme != "postgres" && u.Scheme != "postgresql" {
return fmt.Errorf("DATABASE_URL scheme %q invalid", u.Scheme)
}
if u.Host == "" || u.Path == "" {
return errors.New("DATABASE_URL missing host or dbname")
}
return nil
} Try / catch
cfg, err := postgresconfig.ParsePoolConfig(dsn, prod)
if err != nil {
if strings.Contains(err.Error(), "parse DATABASE_URL") {
var pe *pgconn.ParseConfigError
if errors.As(err, &pe) {
return fmt.Errorf("bad DSN field %q: %w", pe.ConfigName, err)
}
}
return err
} Prevention
- Build DSNs with net/url instead of string concatenation.
- Always URL-encode passwords with special characters.
- Fail fast at startup with a clear message when DATABASE_URL is empty.
- Run a connectivity check (pgxpool ping) as part of readiness probes.
When it happens
Trigger: Calling ParsePoolConfig (or NewPool) with a DATABASE_URL that is empty, missing the postgres:// scheme, has unencoded special characters in the password, contains unknown query parameters, or combines invalid options (e.g. both sslmode and sslcert conflicts pgx rejects).
Common situations: Unescaped password containing @ : / ? characters; DATABASE_URL left empty in the environment; copy-pasted URL with quotes or whitespace; typos like postgre:// or postgresql//; pgx version upgrade adding stricter parameter validation.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- postgres: production DATABASE_URL must be a Postgres URL
- invalid CAVEMAN_LISTEN
- invalid listener
- must be an absolute http(s) URL
- must not contain a query or fragment
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/cb08814339f86b7d.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/postgresconfig/postgresconfig.go:43
// ParsePoolConfig validates the connection string and applies the managed
// database CA to every TLS path. Production rejects sslmode=require because it
// encrypts without authenticating the server; verify-full is mandatory.
func ParsePoolConfig(databaseURL string) (*pgxpool.Config, error) {
production := runtimeenv.IsProduction()
if production {
parsed, err := url.Parse(databaseURL)
if err != nil || (parsed.Scheme != "postgres" && parsed.Scheme != "postgresql") || parsed.Hostname() == "" {
return nil, errors.New("postgres: production DATABASE_URL must be a Postgres URL")
}
if parsed.Query().Get("sslmode") != "verify-full" {
return nil, errors.New("postgres: production DATABASE_URL requires sslmode=verify-full")
}
}
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")
}View on GitHub (pinned to 3ee70a1026)