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

  1. Print/inspect the pgx validation error wrapped inside this message — it names the exact offending parameter.
  2. URL-encode the password: use net/url to build the DSN (url.UserPassword) instead of string concatenation.
  3. Ensure the scheme is postgres:// or postgresql:// and the host/port/dbname are present.
  4. Trim quotes/whitespace and verify the env var is actually set in the deployment.
  5. 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

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


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)