{"record":{"id":"2e6c089b10abf178","repo":"JuliusBrussee/caveman","slug":"postgres-production-database-url-must-be-a-postgr","errorCode":null,"errorMessage":"postgres: production DATABASE_URL must be a Postgres URL","messagePattern":"postgres: production DATABASE_URL must be a Postgres URL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/postgresconfig/postgresconfig.go","lineNumber":34,"sourceCode":"\t\"github.com/JuliusBrussee/caveman/shared/platform/runtimeenv\"\n\t\"github.com/jackc/pgx/v5\"\n\t\"github.com/jackc/pgx/v5/pgxpool\"\n)\n\nconst (\n\tcaEnvironment     = \"CAVE_POSTGRES_CA_CERT\"\n\tcaFileEnvironment = \"CAVE_POSTGRES_CA_CERT_FILE\"\n)\n\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 == \"\" {","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/postgresconfig/postgresconfig.go#L16-L52","documentation":"ParsePoolConfig refuses to build a pgxpool.Config from a DATABASE_URL that is not a parseable postgres:// or postgresql:// URL with a hostname when the process runs in production. The library enforces this because the production TLS posture (verify-full, pinned CA) is only expressible through a URL it can inspect; anything else would silently bypass the security checks.","triggerScenarios":"Calling NewPool/ParsePoolConfig in production (runtimeenv.IsProduction() true) with a URL whose scheme is not postgres/postgresql (e.g. a bare DSN string like 'host=db user=app', or http://), a URL that fails url.Parse, or a URL without a hostname.","commonSituations":"Deploying with a keyword/value DSN copied from local dev, leaving DATABASE_URL set to a docker-compose placeholder, or a staging DATABASE_URL promoted to production unchanged.","solutions":["Rewrite DATABASE_URL as postgres://user:pass@host:5432/dbname with a real hostname","Confirm the deployment actually intends production (RUNTIME_ENV/GO_ENV) — if it is local dev, unset the production flag","URL-encode the connection string (special chars in password break url.Parse)","Add the sslmode=verify-full query parameter too, since this check is followed by the verify-full requirement"],"exampleFix":"// before\nDATABASE_URL=\"host=db.internal user=app dbname=prod sslmode=verify-full\"\n// after\nDATABASE_URL=\"postgres://app:secret@db.internal:5432/prod?sslmode=verify-full\"","handlingStrategy":"validation","validationCode":"u, err := url.Parse(os.Getenv(\"DATABASE_URL\"))\nif err != nil || (u.Scheme != \"postgres\" && u.Scheme != \"postgresql\") || u.Hostname() == \"\" {\n    return fmt.Errorf(\"DATABASE_URL must be postgres://user:pass@host:5432/db\")\n}","typeGuard":"func isPostgresURL(raw string) bool {\n    u, err := url.Parse(raw)\n    return err == nil && (u.Scheme == \"postgres\" || u.Scheme == \"postgresql\") && u.Hostname() != \"\"\n}","tryCatchPattern":"cfg, err := postgresconfig.ParsePoolConfig(databaseURL)\nif err != nil {\n    if strings.Contains(err.Error(), \"must be a Postgres URL\") {\n        return nil, fmt.Errorf(\"DATABASE_URL malformed for production: %w\", err)\n    }\n    return nil, err\n}","preventionTips":["Always use postgres:// URLs, never keyword/value DSNs, in every environment","Validate the connection string shape at config-load/startup time with a startup check","URL-encode passwords containing special characters","Keep local and production DATABASE_URL formats identical to catch drift early"],"tags":["postgres","configuration","production","url"],"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"}