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

  1. 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`.
  2. Check SSL_CERT_FILE/SSL_CERT_DIR env vars point to existing, readable files/directories and unset them if wrong.
  3. 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.
  4. 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

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.

Related errors


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)