docker/cli · error

failed to retrieve context tls info

Error message

failed to retrieve context tls info: %w

What it means

Returned by docker.Endpoint.tlsConfig() (reached via Endpoint.ClientOpts()) when tls.X509KeyPair(cert, key) fails to build a TLS certificate from the context's stored PEM material. The wrapped error is Go stdlib crypto/tls, indicating malformed PEM, a truncated blob, or a key/cert that do not correspond.

Solutions

  1. Re-import a matching cert+key pair into the context (docker context import / context update with correct TLS material).
  2. Verify the pair offline: `openssl x509 -in cert.pem -noout`, `openssl rsa -in key.pem -check`, and compare modulus (`openssl x509 -modulus`, `openssl rsa -modulus`).
  3. Recreate the context's TLS material from known-good files.

Example fix

// before
opts, err := endpoint.ClientOpts() // -> "failed to retrieve context tls info"

// after: validate the pair before relying on the endpoint
if _, err := tls.X509KeyPair(certPEM, keyPEM); err != nil {
    return fmt.Errorf("reload cert/key before use: %w", err)
}
opts, err := endpoint.ClientOpts()
Defensive patterns

Strategy: validation

Validate before calling

// Validate the cert/key pair before constructing client opts.
if ep.TLSData != nil && ep.TLSData.Key != nil && ep.TLSData.Cert != nil {
    if _, err := tls.X509KeyPair(ep.TLSData.Cert, ep.TLSData.Key); err != nil {
        return fmt.Errorf("context TLS material is unusable: %w", err)
    }
}
opts, err := ep.ClientOpts()

Prevention

When it happens

Trigger: Calling endpoint.ClientOpts() for a context whose TLS material has a cert/key pair failing X509KeyPair: key and cert mismatched, cert not actually a certificate, key not in PEM format, or files truncated/corrupted. Only reached when ep.Host is a non-socket host and ep.TLSData has both Key and Cert set.

Common situations: Cert rotation that updated only cert or only key; pointing a context at the wrong key file; manually editing TLS files and truncating PEM headers; DER instead of PEM; copy-paste leaving stray whitespace.

Understand the failure class

Related errors


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

Appendix: source

Thrown at cli/context/docker/load.go:72

			return nil, errors.New("failed to retrieve context tls info: ca.pem seems invalid")
		}
		tlsOpts = append(tlsOpts, func(cfg *tls.Config) {
			cfg.RootCAs = certPool
		})
	}
	if ep.TLSData != nil && ep.TLSData.Key != nil && ep.TLSData.Cert != nil {
		keyBytes := ep.TLSData.Key
		pemBlock, _ := pem.Decode(keyBytes)
		if pemBlock == nil {
			return nil, errors.New("no valid private key found")
		}
		if x509.IsEncryptedPEMBlock(pemBlock) { //nolint:staticcheck // SA1019: x509.IsEncryptedPEMBlock is deprecated, and insecure by design
			return nil, errors.New("private key is encrypted - support for encrypted private keys has been removed, see https://docs.docker.com/go/deprecated/")
		}

		x509cert, err := tls.X509KeyPair(ep.TLSData.Cert, keyBytes)
		if err != nil {
			return nil, fmt.Errorf("failed to retrieve context tls info: %w", err)
		}
		tlsOpts = append(tlsOpts, func(cfg *tls.Config) {
			cfg.Certificates = []tls.Certificate{x509cert}
		})
	}
	if ep.SkipTLSVerify {
		tlsOpts = append(tlsOpts, func(cfg *tls.Config) {
			cfg.InsecureSkipVerify = true
		})
	}
	return tlsconfig.ClientDefault(tlsOpts...), nil
}

// ClientOpts returns a slice of Client options to configure an API client with this endpoint
func (ep *Endpoint) ClientOpts() ([]client.Opt, error) {
	var result []client.Opt
	if ep.Host != "" {
		helper, err := connhelper.GetConnectionHelper(ep.Host)

View on GitHub (pinned to 4f84911bfe)