{"record":{"id":"11d57e48cf9d2603","repo":"JuliusBrussee/caveman","slug":"postgres-s-w","errorCode":null,"errorMessage":"postgres: %s: %w","messagePattern":"postgres: (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/postgresconfig/postgresconfig.go","lineNumber":90,"sourceCode":"\t\tfallback.TLSConfig.RootCAs = roots\n\t\tfallback.TLSConfig.InsecureSkipVerify = false\n\t\tfallback.TLSConfig.ServerName = fallback.Host\n\t}\n\treturn config, nil\n}\n\nfunc caPEMFromEnvironment() (string, error) {\n\tfilePath := strings.TrimSpace(os.Getenv(caFileEnvironment))\n\tdirect := strings.TrimSpace(os.Getenv(caEnvironment))\n\tif filePath != \"\" && direct != \"\" {\n\t\treturn \"\", fmt.Errorf(\"postgres: set only %s or %s, not both\", caFileEnvironment, caEnvironment)\n\t}\n\tif filePath == \"\" {\n\t\treturn direct, nil\n\t}\n\tinfo, err := os.Stat(filePath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"postgres: %s: %w\", caFileEnvironment, err)\n\t}\n\tif !info.Mode().IsRegular() {\n\t\treturn \"\", fmt.Errorf(\"postgres: %s must point to a regular file\", caFileEnvironment)\n\t}\n\tcontents, err := os.ReadFile(filePath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"postgres: read %s: %w\", caFileEnvironment, err)\n\t}\n\treturn strings.TrimSpace(string(contents)), nil\n}\n\n// NewPool constructs a pool from the hardened configuration.\nfunc NewPool(ctx context.Context, databaseURL string) (*pgxpool.Pool, error) {\n\tconfig, err := ParsePoolConfig(databaseURL)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn pgxpool.NewWithConfig(ctx, config)","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/postgresconfig/postgresconfig.go#L72-L108","documentation":"Wrapped error from caPEMFromEnvironment: os.Stat failed on the CA file path taken from the CA-file environment variable (missing file, permission, broken symlink). The environment variable name and the stat error are both carried via the format so the cause is inspectable.","triggerScenarios":"ParsePoolConfig/NewPool called with the CA-file env var set to a path that os.Stat cannot resolve: nonexistent file, typo in path, unreadable parent directory, or dangling symlink.","commonSituations":"CA file not mounted into the container; wrong mount path in Docker/Kubernetes; relative path used but working directory differs; file deleted after image build.","solutions":["Verify the path exists and is readable from the process: `ls -l $POSTGRES_CA_FILE` inside the same container/host.","Mount the CA certificate file into the container and fix the env var to the absolute mounted path.","If the CA is provided inline, remove the file env var and use the direct PEM variable instead.","Check that the user running the process has read permission on the file and its parent directories."],"exampleFix":"// before\nexport POSTGRES_CA_FILE=./certs/ca.crt // relative path, wrong cwd\n// after\nexport POSTGRES_CA_FILE=/etc/app/certs/ca.crt // absolute path, verified mounted","handlingStrategy":"validation","validationCode":"func caFileReadable(path string) error {\n\tif path == \"\" { return nil }\n\tinfo, err := os.Stat(path)\n\tif err != nil { return err }\n\tif !info.Mode().IsRegular() { return fmt.Errorf(\"not a regular file: %s\", path) }\n\tf, err := os.Open(path); if err != nil { return err }\n\tf.Close()\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"if _, err := postgresconfig.ParsePoolConfig(dbURL); err != nil {\n\tif errors.Is(err, os.ErrNotExist) {\n\t\tlog.Fatalf(\"CA file missing: %v\", err)\n\t}\n\treturn err\n}","preventionTips":["Always use absolute paths for the CA file env var.","Ensure the CA file is mounted/copied into the container image or volume before startup.","Verify mounts with a readiness check that stats the file."],"tags":["postgres","tls","file","environment"],"backgroundTag":"file-not-found","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"}