ory/hydra · error

unable to load X509 key pair from files: %v

Error message

unable to load X509 key pair from files: %v

What it means

Certificate resolves TLS material from paths or base64 strings. When both a cert path and key path are provided, it calls tls.LoadX509KeyPair; this error wraps any failure reading or parsing those files — missing files, permission denied, or invalid/unmatched PEM contents.

Source

Thrown at oryx/tlsx/cert.go:118

	certPEMBase64, keyPEMBase64 string,
	certPath, keyPath string,
) ([]tls.Certificate, error) {
	if certPEMBase64 == "" && keyPEMBase64 == "" && certPath == "" && keyPath == "" {
		return nil, errors.WithStack(ErrNoCertificatesConfigured)
	}

	if certPEMBase64 != "" && keyPEMBase64 != "" {
		cert, err := CertificateFromBase64(certPEMBase64, keyPEMBase64)
		if err != nil {
			return nil, errors.WithStack(err)
		}
		return []tls.Certificate{cert}, nil
	}

	if certPath != "" && keyPath != "" {
		cert, err := tls.LoadX509KeyPair(certPath, keyPath)
		if err != nil {
			return nil, fmt.Errorf("unable to load X509 key pair from files: %v", err)
		}
		return []tls.Certificate{cert}, nil
	}

	return nil, errors.WithStack(ErrInvalidCertificateConfiguration)
}

type CertFunc = func(*tls.ClientHelloInfo) (*tls.Certificate, error)

// GetCertificate returns a function for use with
// "net/tls".Config.GetCertificate.
//
// The certificate and private key are read from the specified filesystem paths.
// The certificate file is watched for changes, upon which the cert+key are
// reloaded in the background. Errors during reloading are deduplicated and
// reported through the errs channel if it is not nil. When the provided context
// is canceled, background reloading stops and the errs channel is closed.
//

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Verify both files exist and are readable by the process (ls -l, check mount/secret volume)
  2. Convert files to PEM format if they are DER (openssl x509 -inform der -outform pem)
  3. Ensure cert and key are a matching pair (compare moduli with openssl)
  4. Use absolute paths or set the correct working directory; re-check config/env values

Example fix

// before
cert, err := Certificate("/etc/ssl/cert.der", "/etc/ssl/key.pem", "", "")
// after (convert DER to PEM first)
// openssl x509 -inform der -in cert.der -out cert.pem
cert, err := Certificate("/etc/ssl/cert.pem", "/etc/ssl/key.pem", "", "")
Defensive patterns

Strategy: validation

Validate before calling

func checkCertFiles(certPath, keyPath string) error {
    for _, p := range []string{certPath, keyPath} {
        if fi, err := os.Stat(p); err != nil || fi.IsDir() {
            return fmt.Errorf("missing/unreadable %s", p)
        }
    }
    _, err := tls.LoadX509KeyPair(certPath, keyPath)
    return err
}

Try / catch

certs, err := tlsx.Certificate(certPath, keyPath, "", "")
if err != nil && strings.Contains(err.Error(), "X509 key pair from files") {
    log.WithError(err).Errorf("check paths %q / %q exist, are PEM, and form a matching pair", certPath, keyPath)
    return err
}

Prevention

When it happens

Trigger: Calling Certificate with non-empty certPath and keyPath where the files do not exist, are unreadable, contain non-PEM data, or the key does not match the certificate.

Common situations: Wrong paths in config (relative path vs working directory), container missing the mounted secret, permissions after secret rotation, DER instead of PEM, or cert/key pair mismatch after cert renewal.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/a405aa8d1b221588. Report an issue: GitHub.