nats-io/nats-server · error

error parsing certificate %d/%d: %v

Error message

error parsing certificate %d/%d: %v

What it means

Returned by NATS server TLS option setup (ProcessOptionsFile/TLS config path in server/opts.go) after a key pair loads but the embedded certificate fails x509.ParseCertificate. tls.LoadX509KeyPair only checks PEM block structure; this second parse decodes the actual X.509 ASN.1 DER, so a structurally valid PEM wrapping corrupt or non-certificate DER fails here. The index i+1/len identifies which certificate pair in tc.Certificates failed.

Source

Thrown at server/opts.go:5864

			return nil, fmt.Errorf("error parsing certificate: %v", err)
		}
		config.Certificates = []tls.Certificate{cert}
	case tc.CertStore != certstore.STOREEMPTY:
		err := certstore.TLSConfig(tc.CertStore, tc.CertMatchBy, tc.CertMatch, tc.CaCertsMatch, tc.CertMatchSkipInvalid, &config)
		if err != nil {
			return nil, err
		}
	case tc.Certificates != nil:
		// Multiple certificate support.
		config.Certificates = make([]tls.Certificate, len(tc.Certificates))
		for i, certPair := range tc.Certificates {
			cert, err := tls.LoadX509KeyPair(certPair.CertFile, certPair.KeyFile)
			if err != nil {
				return nil, fmt.Errorf("error parsing X509 certificate/key pair %d/%d: %v", i+1, len(tc.Certificates), err)
			}
			cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
			if err != nil {
				return nil, fmt.Errorf("error parsing certificate %d/%d: %v", i+1, len(tc.Certificates), err)
			}
			config.Certificates[i] = cert
		}
	}

	// Require client certificates as needed
	if tc.Verify {
		config.ClientAuth = tls.RequireAndVerifyClientCert
	}
	// Add in CAs if applicable.
	if tc.CaFile != _EMPTY_ {
		rootPEM, err := os.ReadFile(tc.CaFile)
		if err != nil || rootPEM == nil {
			return nil, err
		}
		pool := x509.NewCertPool()
		ok := pool.AppendCertsFromPEM(rootPEM)
		if !ok {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Verify the cert file with `openssl x509 -in cert.pem -text -noout`; regenerate or re-export it if it fails.
  2. Ensure cert and key files were not swapped: the CertFile must contain -----BEGIN CERTIFICATE----- blocks.
  3. Re-copy/download the certificate from the issuing source and confirm file integrity (checksum).
  4. Re-issue the certificate from the CA if the DER is genuinely corrupt.

Example fix

// before: cert.pem contains a CSR
CertFile: server.csr
// after
CertFile: server.crt  // openssl x509 -in server.crt -noout succeeds
Defensive patterns

Strategy: validation

Validate before calling

// Validate each cert file before configuring the server
for _, pair := range certPairs {
    pemBytes, err := os.ReadFile(pair.CertFile)
    if err != nil { return err }
    block, _ := pem.Decode(pemBytes)
    if block == nil || block.Type != "CERTIFICATE" {
        return fmt.Errorf("%s: no CERTIFICATE PEM block", pair.CertFile)
    }
    if _, err := x509.ParseCertificate(block.Bytes); err != nil {
        return fmt.Errorf("%s: invalid certificate: %v", pair.CertFile, err)
    }
}

Type guard

func isPEMCertificate(b []byte) bool {
    block, _ := pem.Decode(b)
    if block == nil || block.Type != "CERTIFICATE" { return false }
    _, err := x509.ParseCertificate(block.Bytes)
    return err == nil
}

Try / catch

cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
    return fmt.Errorf("loading TLS key pair %s: %w", certFile, err)
}
if cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0]); err != nil {
    return fmt.Errorf("certificate %s is corrupt, re-export it: %w", certFile, err)
}

Prevention

When it happens

Trigger: Configuring server TLS via the certificates option with a cert file whose leaf certificate bytes are corrupt, truncated, or not a real certificate (e.g. a CSR or a private key pasted into the cert file), while the key pair itself still loads.

Common situations: Manually concatenated PEM files, certificates re-saved through editors that mangled base64, wrong file swapped in (key instead of cert), corrupted files from interrupted scp/copy, or certs exported in a non-PEM container then renamed .crt.

Understand the failure class

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/8021fbb79c859c33. Report an issue: GitHub.