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
- Provide a PEM-encoded private key: convert DER with 'openssl rsa -inform DER -outform PEM -in key.der -out key.pem'.
- Verify: 'openssl pkey -in key.pem -noout' should succeed.
- 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
- Generate keys in PEM format (openssl defaults to PEM).
- Convert DER keys with 'openssl rsa -inform DER -outform PEM'.
- Verify with 'openssl pkey -in key.pem -noout'.
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
- failed to retrieve context tls info: ca.pem seems invalid
- private key is encrypted - support for encrypted private…
- archive format is invalid
- : parsing
- failed to retrieve context tls info
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)