JuliusBrussee/caveman · error
load system certificate pool
Error message
load system certificate pool: %w
What it means
PoolOf starts from the operating system's root certificate pool via x509.SystemCertPool() and appends custom certificates. If the system pool cannot be loaded (platform-level failure reading the OS trust store), the error is wrapped with this message. The library throws it because it cannot establish a trust baseline without the system roots.
Solutions
- Install the CA certificates: on Debian/Ubuntu `apt-get install -y ca-certificates`, on Alpine `apk add ca-certificates`, on RHEL `yum install ca-certificates`.
- Check SSL_CERT_FILE/SSL_CERT_DIR env vars point to existing, readable files/directories and unset them if wrong.
- If the platform truly has no system pool, load a bundled PEM file via cabundle.Certificates and pass it to PoolOf — or use x509.NewCertPool() seeded from a known-good bundle.
- Upgrade Go if on an old version where SystemCertPool errored on Windows.
Example fix
// Dockerfile (before) FROM golang:1.22 AS build FROM scratch // after FROM golang:1.22 AS build FROM scratch COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
Defensive patterns
Strategy: fallback
Validate before calling
if _, err := x509.SystemCertPool(); err != nil {
// fall back to a bundled CA file before calling PoolOf
certs, cerr := cabundle.Certificates("/app/certs/ca-bundle.pem")
if cerr != nil { return cerr }
pool := x509.NewCertPool()
for _, c := range certs { pool.AddCert(c) }
} Try / catch
pool, err := cabundle.PoolOf(extra)
if err != nil && strings.Contains(err.Error(), "load system certificate pool") {
return fmt.Errorf("OS trust store unavailable (install ca-certificates or set SSL_CERT_FILE): %w", err)
} Prevention
- Install ca-certificates in every container image, including scratch/distroless builds (copy /etc/ssl/certs).
- Do not set SSL_CERT_FILE/SSL_CERT_DIR to nonexistent paths.
- Ship a known-good fallback CA bundle for environments without a system store.
When it happens
Trigger: Calling PoolOf (directly or through Pool/loadRootCAs) on a system where x509.SystemCertPool() fails: missing /etc/ssl/certs on minimal Linux images, an unreadable or corrupt CA bundle path (SSL_CERT_FILE/SSL_CERT_DIR pointing nowhere), or unsupported platforms (notably some Windows/older Go versions returned an error for SystemCertPool).
Common situations: Scratch/distroless containers without ca-certificates installed; SSL_CERT_FILE set to a nonexistent path; alpine images lacking the ca-certificates package; statically linked builds on platforms without a discoverable trust store.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- postgres: or is required in production
- postgres
- postgres: set only or , not both
- : certificate is unparseable, so the bundle is incomplete…
- : trailing PEM block is truncated after certificate(s), so…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/be32f83641bd25b2.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/cabundle/cabundle.go:34
// private CA is trusted for the inspected ones. Any unusable bundle fails the
// whole pool CLOSED; see Certificates.
func Pool(paths ...string) (*x509.CertPool, error) {
var certs []*x509.Certificate
for _, path := range paths {
loaded, err := Certificates(path)
if err != nil {
return nil, err
}
certs = append(certs, loaded...)
}
return PoolOf(certs)
}
// PoolOf returns the system pool with certs appended.
func PoolOf(certs []*x509.Certificate) (*x509.CertPool, error) {
roots, err := x509.SystemCertPool()
if err != nil {
return nil, fmt.Errorf("load system certificate pool: %w", err)
}
for _, cert := range certs {
roots.AddCert(cert)
}
return roots, nil
}
// Certificates parses every certificate in the PEM bundle at path.
//
// The bundle is parsed block by block instead of via CertPool.AppendCertsFromPEM,
// which reports success as soon as ONE certificate parses and silently drops the
// rest. A truncated or corrupt bundle would then be half-trusted: the endpoints
// whose issuer survived keep verifying and the ones whose issuer was dropped fail
// later, looking like a network fault. Any unusable certificate block — or a
// trailing PEM header with no complete block behind it — rejects the whole
// bundle instead, and nothing from it is returned.
func Certificates(path string) ([]*x509.Certificate, error) {
bundle, err := os.ReadFile(path)View on GitHub (pinned to 3ee70a1026)