nats-io/nats-server · error

failed to parse cert

Error message

failed to parse cert: %v

What it means

After the CA file, getOCSPIssuer parses each raw certificate in the provided chain with x509.ParseCertificate. A chain entry that is not a valid DER-encoded X.509 certificate aborts startup of the OCSP monitor with this wrapped error.

Solutions

  1. Build the chain from tls.ConnectionState().PeerCertificates instead of raw byte slices
  2. Validate each file's certs with `openssl crl2pkcs7 -nocrl -certfile bundle.pem | openssl pkcs7 -print_certs -noout`
  3. Remove any non-certificate text (keys, garbage) from the bundle file

Example fix

// before
chain := [][]byte{ rawFileBytes }  // not per-cert DER
// after
chain := [][]byte{}
for _, c := range conn.PeerCertificates { chain = append(chain, c.Raw) }
Defensive patterns

Strategy: validation

Validate before calling

for i, raw := range chain {
    if _, err := x509.ParseCertificate(raw); err != nil {
        return fmt.Errorf("chain[%d] is not a valid DER cert: %w", i, err)
    }
}

Prevention

When it happens

Trigger: NewOCSPMonitor called with a chain slice containing bytes that fail x509.ParseCertificate (empty entries, TLS handshake leftovers, malformed DER).

Common situations: Chain bytes harvested incorrectly from tls.ConnectionState (indexing PeerCertificates wrong), or a corrupted/hand-edited bundle file.

Understand the failure class

Related errors


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

Appendix: source

Thrown at server/ocsp.go:957

	var trustedCAs []*x509.Certificate
	var certBundle []*x509.Certificate
	var err error

	// FIXME(tgb): extend if pluggable CA store provider added to NATS (i.e. other than PEM file)

	// Non-system default CA trust store passed
	if caFile != _EMPTY_ {
		trustedCAs, err = parseCertPEM(caFile)
		if err != nil {
			return nil, fmt.Errorf("failed to parse ca_file: %v", err)
		}
	}

	// Specify bundled intermediate CA store
	for _, certBytes := range chain {
		cert, err := x509.ParseCertificate(certBytes)
		if err != nil {
			return nil, fmt.Errorf("failed to parse cert: %v", err)
		}
		certBundle = append(certBundle, cert)
	}

	issuer, err = getOCSPIssuerLocally(trustedCAs, certBundle)
	if err != nil || issuer == nil {
		return nil, fmt.Errorf("no issuers found")
	}

	if !issuer.IsCA {
		return nil, fmt.Errorf("%s invalid ca basic constraints: is not ca", issuer.Subject)
	}
	return issuer, nil
}

func ocspStatusString(n int) string {
	switch n {
	case ocsp.Good:

View on GitHub (pinned to 3a66a489d2)