docker/cli · error

no valid private key found

Error message

no valid private key found

What it means

Returned by Endpoint.tlsConfig when pem.Decode on the stored key bytes returns a nil block, meaning the data is not PEM-encoded at all. The key material must be a PEM block (e.g. '-----BEGIN PRIVATE KEY-----') to be usable for a client certificate; non-PEM or empty data is rejected before any key parsing.

Solutions

  1. Provide a PEM-encoded private key: convert DER with 'openssl rsa -inform DER -outform PEM -in key.der -out key.pem'.
  2. Verify: 'openssl pkey -in key.pem -noout' should succeed.
  3. Recreate the context using a matching cert/key PEM pair.
Defensive patterns

Strategy: validation

Validate before calling

if pem.Decode(keyBytes) == nil {
    return errors.New("key file is not PEM-encoded")
}

Type guard

func isPEMKey(b []byte) bool {
    block, _ := pem.Decode(b)
    return block != nil
}

Prevention

When it happens

Trigger: A context's key file (key.pem) is empty, contains DER-encoded binary, or holds non-key content. Importing a context archive where the key file is missing/corrupt but the cert is present.

Common situations: The key.pem was generated in DER format instead of PEM. The file was truncated during copy. A cert was placed where the key should be.

Related errors


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

Appendix: source

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

	if ep.TLSData == nil && !ep.SkipTLSVerify {
		// there is no specific tls config
		return nil, nil
	}
	var tlsOpts []func(*tls.Config)
	if ep.TLSData != nil && ep.TLSData.CA != nil {
		certPool := x509.NewCertPool()
		if !certPool.AppendCertsFromPEM(ep.TLSData.CA) {
			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
		})
	}

View on GitHub (pinned to 4f84911bfe)