JuliusBrussee/caveman · error
ca_bundle
Error message
ca_bundle: %w
What it means
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.
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
Example fix
// before (caveman.yaml) ca_bundle: ./mitm.pem # file missing // after ca_bundle: /etc/caveman/corp-mitm-chain.pem
Defensive patterns
Strategy: validation
Validate before calling
path := cfg.CABundle
if fi, err := os.Stat(path); err != nil || fi.IsDir() {
return fmt.Errorf("ca_bundle %q is not a readable file", path)
}
if _, err := os.ReadFile(path); err != nil {
return fmt.Errorf("ca_bundle unreadable: %w", err)
} Try / catch
if err := cfg.loadRootCAs(); err != nil {
var pe *fs.PathError
if errors.As(err, &pe) {
logger.Error("ca_bundle path problem", "path", pe.Path, "op", pe.Op)
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- ca bundle
- postgres: contains no valid certificate
- %s: %w
- postgres: CA certificate configured while TLS is disabled
- postgres: must point to a regular file
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/a10835fae4202471.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/internal/config/config.go:208
if err := cfg.loadRootCAs(); err != nil {
return Config{}, err
}
return cfg, nil
}
// inheritedCABundleEnv names the CA bundle variables other toolchains already
// read: Go/OpenSSL, Python requests, and Node (which Claude Code runs on).
var inheritedCABundleEnv = []string{"SSL_CERT_FILE", "REQUESTS_CA_BUNDLE", "NODE_EXTRA_CA_CERTS"}
// loadRootCAs builds the provider trust store. It stays nil — Go's default
// verification — when no bundle is configured, so the common case keeps the
// platform verifier untouched.
func (c *Config) loadRootCAs() error {
var certs []*x509.Certificate
if c.CABundle = strings.TrimSpace(c.CABundle); c.CABundle != "" {
loaded, err := cabundle.Certificates(c.CABundle)
if err != nil {
return fmt.Errorf("ca_bundle: %w", err)
}
certs = append(certs, loaded...)
}
for _, name := range inheritedCABundleEnv {
path := strings.TrimSpace(env.String(name, ""))
if path == "" {
continue
}
loaded, err := cabundle.Certificates(path)
if err != nil {
c.SkippedCABundles = append(c.SkippedCABundles, SkippedCABundle{Env: name, Error: err.Error()})
continue
}
certs = append(certs, loaded...)
}
if len(certs) == 0 {
return nil
}View on GitHub (pinned to 3ee70a1026)