cilium/cilium · error
certificate and private key are both required, but only one
Error message
certificate and private key are both required, but only one was provided
What it means
ErrInvalidKeypair in pkg/crypto/certloader/reloader.go is returned when constructing a FileReloader (NewFileReloader, via NewFileReloaderReady) when exactly one of the certificate and private key files is provided but not the other. The certloader requires the pair to be complete; a lone cert or lone key cannot be used to establish TLS.
Source
Thrown at pkg/crypto/certloader/reloader.go:37
type FileReloader struct {
// caFiles, certFile, and privkeyFile are constants for the FileReloader's
// lifetime, thus accessing them doesn't require acquiring the mutex.
caFiles []string
certFile string
privkeyFile string
mutex lock.Mutex
// fields below should only be accessed with mutex acquired as they may be
// updated concurrently.
caCertPool *x509.CertPool
caCertPoolGeneration uint // incremented when caCertPool is reloaded
keypair *tls.Certificate
keypairGeneration uint // incremented when keypair is reloaded
}
var (
// ErrInvalidKeypair is returned when either the certificate or its
// corresponding private key is missing.
ErrInvalidKeypair = errors.New("certificate and private key are both required, but only one was provided")
)
// NewFileReloaderReady create and returns a FileReloader using the given file.
// The files are already loaded when this function returns, thus the returned
// FileReloader is readily usable.
func NewFileReloaderReady(caFiles []string, certFile, privkeyFile string) (*FileReloader, error) {
r, err := NewFileReloader(caFiles, certFile, privkeyFile)
if err != nil {
return nil, err
}
// load the files for the first time.
if _, _, err := r.Reload(); err != nil {
return nil, err
}
return r, nil
}View on GitHub (pinned to ac7b90affa)
Solutions
- Provide both certFile and privkeyFile (e.g. --tls-cert + --tls-key or the corresponding Helm values)
- Verify the mounted Kubernetes Secret contains both tls.crt and tls.key entries
- Fix the file path typo causing one of the pair to be missing
- If you intend no TLS, clear both flags rather than only one
Example fix
// before reloader, err := certloader.NewFileReloader(nil, "/certs/tls.crt", "") // after reloader, err := certloader.NewFileReloader(nil, "/certs/tls.crt", "/certs/tls.key")
Defensive patterns
Strategy: validation
Validate before calling
func validateKeypair(certFile, privkeyFile string) error {
if (certFile == "") != (privkeyFile == "") {
return certloader.ErrInvalidKeypair
}
if certFile != "" {
if _, err := os.Stat(certFile); err != nil { return err }
if _, err := os.Stat(privkeyFile); err != nil { return err }
}
return nil
} Type guard
func keypairComplete(certFile, privkeyFile string) bool { return (certFile == "") == (privkeyFile == "") } Try / catch
r, err := certloader.NewFileReloader(caFiles, certFile, privkeyFile)
if errors.Is(err, certloader.ErrInvalidKeypair) {
return fmt.Errorf("TLS keypair incomplete: cert=%q key=%q", certFile, privkeyFile)
} Prevention
- Always set cert and key flags as a pair (Helm values grouped together)
- Mount Secrets containing both tls.crt and tls.key, and verify after mounting
- Run flag validation (like validateMutualTLSFlags) before starting components
- Watch for partial Secret updates in Kubernetes and re-sync volumes
When it happens
Trigger: Calling NewFileReloader/NewFileReloaderReady (used by validateMutualTLSFlags) with certFile set but privkeyFile empty, or privkeyFile set but certFile empty. Both empty may be allowed (non-mTLS); the mismatch is not.
Common situations: Incomplete Helm/CLI flags (e.g. setting --tls-cert but forgetting --tls-key); secret mounted with only one of the two files; typo in one file path causing the loader to see it as missing; partial Secret update in Kubernetes.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- certificate file path is required
- failed waiting for TLS certificates to become available: %w
- endpoint manager is not loaded
- trust bundle not yet available
- private key file path is required
AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31).
Data as JSON: /api/errors/a473c20b3e5be62c.
Report an issue: GitHub.