{"record":{"id":"a10835fae4202471","repo":"JuliusBrussee/caveman","slug":"ca-bundle-w","errorCode":null,"errorMessage":"ca_bundle: %w","messagePattern":"ca_bundle: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/config/config.go","lineNumber":208,"sourceCode":"\tif err := cfg.loadRootCAs(); err != nil {\n\t\treturn Config{}, err\n\t}\n\treturn cfg, nil\n}\n\n// inheritedCABundleEnv names the CA bundle variables other toolchains already\n// read: Go/OpenSSL, Python requests, and Node (which Claude Code runs on).\nvar inheritedCABundleEnv = []string{\"SSL_CERT_FILE\", \"REQUESTS_CA_BUNDLE\", \"NODE_EXTRA_CA_CERTS\"}\n\n// loadRootCAs builds the provider trust store. It stays nil — Go's default\n// verification — when no bundle is configured, so the common case keeps the\n// platform verifier untouched.\nfunc (c *Config) loadRootCAs() error {\n\tvar certs []*x509.Certificate\n\tif c.CABundle = strings.TrimSpace(c.CABundle); c.CABundle != \"\" {\n\t\tloaded, err := cabundle.Certificates(c.CABundle)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"ca_bundle: %w\", err)\n\t\t}\n\t\tcerts = append(certs, loaded...)\n\t}\n\tfor _, name := range inheritedCABundleEnv {\n\t\tpath := strings.TrimSpace(env.String(name, \"\"))\n\t\tif path == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tloaded, err := cabundle.Certificates(path)\n\t\tif err != nil {\n\t\t\tc.SkippedCABundles = append(c.SkippedCABundles, SkippedCABundle{Env: name, Error: err.Error()})\n\t\t\tcontinue\n\t\t}\n\t\tcerts = append(certs, loaded...)\n\t}\n\tif len(certs) == 0 {\n\t\treturn nil\n\t}","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/internal/config/config.go#L190-L226","documentation":"loadRootCAs (proxy/internal/config/config.go:208) loads the ca_bundle file via the shared cabundle parser to build a custom TLS trust store (for MITM/inspection setups). If reading or parsing the configured bundle fails, the underlying error is wrapped as 'ca_bundle: <err>' and startup aborts — fail-closed, since a broken trust store would silently break provider TLS.","triggerScenarios":"ca_bundle in caveman.yaml (or CAVE_CA_BUNDLE) points to a nonexistent file, an unreadable path, a file with invalid PEM, or an empty/corrupt bundle.","commonSituations":"Corporate MITM proxy cert exported to the wrong path; PEM file with only a private key or truncated chain; wrong permissions after copying; path relative to the wrong working directory; env var pointing at a file deleted by a cleanup job.","solutions":["Verify the file exists and is readable at the configured path (absolute path is safest)","Ensure the file is a valid PEM certificate chain (certificates, not a private key)","Test parsing: e.g. openssl x509 -in bundle.pem -noout to see if it is valid PEM","If you do not need custom roots, clear ca_bundle and the inherited CA env vars"],"exampleFix":"// before (caveman.yaml)\nca_bundle: ./mitm.pem   # file missing\n// after\nca_bundle: /etc/caveman/corp-mitm-chain.pem","handlingStrategy":"validation","validationCode":"path := cfg.CABundle\nif fi, err := os.Stat(path); err != nil || fi.IsDir() {\n    return fmt.Errorf(\"ca_bundle %q is not a readable file\", path)\n}\nif _, err := os.ReadFile(path); err != nil {\n    return fmt.Errorf(\"ca_bundle unreadable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := cfg.loadRootCAs(); err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) {\n        logger.Error(\"ca_bundle path problem\", \"path\", pe.Path, \"op\", pe.Op)\n    }\n    return err\n}","preventionTips":["Use absolute paths for ca_bundle","Verify the bundle exists and is readable before deploy (test with openssl x509 -in f -noout)","Keep bundles under a stable directory excluded from cleanup jobs"],"tags":["tls","config","ca-bundle","certificate"],"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"}