{"record":{"id":"cb08814339f86b7d","repo":"JuliusBrussee/caveman","slug":"postgres-parse-database-url-w","errorCode":null,"errorMessage":"postgres: parse DATABASE_URL: %w","messagePattern":"postgres: parse DATABASE_URL: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"shared/platform/postgresconfig/postgresconfig.go","lineNumber":43,"sourceCode":"\n// ParsePoolConfig validates the connection string and applies the managed\n// database CA to every TLS path. Production rejects sslmode=require because it\n// encrypts without authenticating the server; verify-full is mandatory.\nfunc ParsePoolConfig(databaseURL string) (*pgxpool.Config, error) {\n\tproduction := runtimeenv.IsProduction()\n\tif production {\n\t\tparsed, err := url.Parse(databaseURL)\n\t\tif err != nil || (parsed.Scheme != \"postgres\" && parsed.Scheme != \"postgresql\") || parsed.Hostname() == \"\" {\n\t\t\treturn nil, errors.New(\"postgres: production DATABASE_URL must be a Postgres URL\")\n\t\t}\n\t\tif parsed.Query().Get(\"sslmode\") != \"verify-full\" {\n\t\t\treturn nil, errors.New(\"postgres: production DATABASE_URL requires sslmode=verify-full\")\n\t\t}\n\t}\n\n\tconfig, err := pgxpool.ParseConfig(databaseURL)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"postgres: parse DATABASE_URL: %w\", err)\n\t}\n\tcaPEM, err := caPEMFromEnvironment()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif production && caPEM == \"\" {\n\t\treturn nil, fmt.Errorf(\"postgres: %s or %s is required in production\", caEnvironment, caFileEnvironment)\n\t}\n\tif caPEM == \"\" {\n\t\treturn config, nil\n\t}\n\troots := x509.NewCertPool()\n\tif !roots.AppendCertsFromPEM([]byte(caPEM)) {\n\t\treturn nil, fmt.Errorf(\"postgres: %s contains no valid certificate\", caEnvironment)\n\t}\n\tif config.ConnConfig.TLSConfig == nil {\n\t\treturn nil, errors.New(\"postgres: CA certificate configured while TLS is disabled\")\n\t}","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/postgresconfig/postgresconfig.go#L25-L61","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\ndsn := fmt.Sprintf(\"postgres://user:%s@db:5432/app\", password) // password contains @/\n// after\nu := &url.URL{Scheme: \"postgres\", User: url.UserPassword(\"user\", password), Host: \"db:5432\", Path: \"/app\"}\ndsn := u.String()","handlingStrategy":"validation","validationCode":"func validateDatabaseURL(raw string) error {\n    if strings.TrimSpace(raw) == \"\" {\n        return errors.New(\"DATABASE_URL is empty\")\n    }\n    u, err := url.Parse(raw)\n    if err != nil {\n        return fmt.Errorf(\"DATABASE_URL not parseable: %w\", err)\n    }\n    if u.Scheme != \"postgres\" && u.Scheme != \"postgresql\" {\n        return fmt.Errorf(\"DATABASE_URL scheme %q invalid\", u.Scheme)\n    }\n    if u.Host == \"\" || u.Path == \"\" {\n        return errors.New(\"DATABASE_URL missing host or dbname\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"cfg, err := postgresconfig.ParsePoolConfig(dsn, prod)\nif err != nil {\n    if strings.Contains(err.Error(), \"parse DATABASE_URL\") {\n        var pe *pgconn.ParseConfigError\n        if errors.As(err, &pe) {\n            return fmt.Errorf(\"bad DSN field %q: %w\", pe.ConfigName, err)\n        }\n    }\n    return err\n}","preventionTips":["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."],"tags":["postgres","configuration","url","database"],"backgroundTag":"invalid-url-format","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}