docker/cli · error · invalidParameterErr

unable to get system cert pool

Error message

unable to get system cert pool: %w

What it means

Returned by loadTLSConfig when x509.SystemCertPool() fails while processing a .crt file in the registry TLS directory. SystemCertPool reads the OS trust store; failure (wrapped with invalidParam) means the CLI cannot establish a trusted TLS root set for the registry.

Solutions

  1. Install the OS CA bundle (e.g. `apt-get install ca-certificates`, `apk add ca-certificates`).
  2. Ensure $SSL_CERT_FILE / $SSL_CERT_DIR point to existing, readable files/dirs.
  3. Fix permissions on the system trust store (/etc/ssl/certs).
  4. Provide the full CA chain via the .crt in certs.d so SystemCertPool fallback is supplemented.

Example fix

# before (alpine, no ca-certificates)
docker pull myregistry/app
# unable to get system cert pool
# after
apk add --no-cache ca-certificates
docker pull myregistry/app
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure a trust store exists before configuring registry TLS
if _, err := x509.SystemCertPool(); err != nil { installCABundle() }

Try / catch

if strings.Contains(err.Error(), "unable to get system cert pool") { installCaCertificates(); retry }

Prevention

When it happens

Trigger: Configuring a registry with TLS certs (a .crt in /etc/docker/certs.d/<host>/) on a system where the trust store cannot be loaded — missing CA bundle file, unreadable /etc/ssl/certs, minimal/container OS without ca-certificates.

Common situations: Scratch/minimal containers or Alpine without ca-certificates installed; custom $SSL_CERT_FILE pointing at a missing file; permission denied on system CA bundle; unusual platform without a known cert pool.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/7570e9b47d7899ef. Report an issue: GitHub.

Appendix: source

Thrown at internal/registry/registry.go:80

func loadTLSConfig(ctx context.Context, directory string, tlsConfig *tls.Config) error {
	fs, err := os.ReadDir(directory)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return invalidParam(err)
	}

	for _, f := range fs {
		if ctx.Err() != nil {
			return ctx.Err()
		}
		switch filepath.Ext(f.Name()) {
		case ".crt":
			if tlsConfig.RootCAs == nil {
				systemPool, err := x509.SystemCertPool()
				if err != nil {
					return invalidParam(fmt.Errorf("unable to get system cert pool: %w", err))
				}
				tlsConfig.RootCAs = systemPool
			}
			fileName := filepath.Join(directory, f.Name())
			log.G(ctx).Debugf("crt: %s", fileName)
			data, err := os.ReadFile(fileName)
			if err != nil {
				return err
			}
			tlsConfig.RootCAs.AppendCertsFromPEM(data)
		case ".cert":
			certName := f.Name()
			keyName := certName[:len(certName)-5] + ".key"
			log.G(ctx).Debugf("cert: %s", filepath.Join(directory, certName))
			if !hasFile(fs, keyName) {
				return invalidParamf("missing key %s for client certificate %s. CA certificates must use the extension .crt", keyName, certName)
			}
			cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName))

View on GitHub (pinned to 4f84911bfe)