slackhq/nebula · error

ErrExpired

ErrExpired

Error message

certificate is expired

What it means

ErrExpired indicates a CA certificate being loaded or verified has passed its NotAfter time. NewCAPoolFromPEMReader/AddCA tolerate expired CAs (skipping them) but report ErrExpired at the end; verify returns it when the candidate certificate itself is expired.

Source

Thrown at cert/errors.go:11

package cert

import (
	"errors"
	"fmt"
)

var (
	ErrBadFormat                  = errors.New("bad wire format")
	ErrRootExpired                = errors.New("root certificate is expired")
	ErrExpired                    = errors.New("certificate is expired")
	ErrNotCA                      = errors.New("certificate is not a CA")
	ErrNotSelfSigned              = errors.New("certificate is not self-signed")
	ErrBlockListed                = errors.New("certificate is in the block list")
	ErrFingerprintMismatch        = errors.New("certificate fingerprint did not match")
	ErrSignatureMismatch          = errors.New("certificate signature did not match")
	ErrInvalidPublicKey           = errors.New("invalid public key")
	ErrInvalidPrivateKey          = errors.New("invalid private key")
	ErrPublicPrivateCurveMismatch = errors.New("public key does not match private key curve")
	ErrPublicPrivateKeyMismatch   = errors.New("public key and private key are not a pair")
	ErrPrivateKeyEncrypted        = errors.New("private key must be decrypted")
	ErrCaNotFound                 = errors.New("could not find ca for the certificate")
	ErrUnknownVersion             = errors.New("certificate version unrecognized")
	ErrCertPubkeyPresent          = errors.New("certificate has unexpected pubkey present")
	ErrCurveMismatch              = errors.New("certificate curve does not match CA")

	ErrInvalidPEMBlock                   = errors.New("input did not contain a valid PEM encoded block")
	ErrInvalidPEMCertificateBanner       = errors.New("bytes did not contain a proper certificate banner")
	ErrInvalidPEMX25519PublicKeyBanner   = errors.New("bytes did not contain a proper X25519 public key banner")

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Replace the expired CA/leaf certificate with one whose NotAfter is in the future.
  2. Check pool.IsExpired / the combined pool+ErrExpired result: the pool is still usable if other CAs are valid; filter or renew expired entries.
  3. Fix clock/NTP sync on the host.
  4. Re-sign the offending certificate from a current CA.

Example fix

// before
pool, err := cert.NewCAPoolFromBytes(caPem)
if err != nil { return err } // rejects pool even though some CAs are valid

// after
pool, err := cert.NewCAPoolFromBytes(caPem)
if pool == nil { return err }
if errors.Is(err, cert.ErrExpired) { log.Warn("some CAs expired; renew ca.crt") }
Defensive patterns

Strategy: try-catch

Validate before calling

if c.Expired(time.Now()) {
    log.Warn("certificate %s is expired", c.Name())
}

Type guard

func isCurrent(c cert.Certificate, now time.Time) bool {
    return !c.Expired(now)
}

Try / catch

pool, err := cert.NewCAPoolFromBytes(caPem)
if pool == nil {
    return err
}
if errors.Is(err, cert.ErrExpired) {
    log.Warn("pool contains expired CAs; renew them")
}

Prevention

When it happens

Trigger: NewCAPoolFromBytes/FromPEMReader encounters expired CA entries in the PEM (returns pool plus ErrExpired via cert/ca_pool.go:77); pool.AddCA or pool.verify called with an expired certificate; returned from verify when c.Expired(now) is true.

Common situations: ca.crt containing stale CAs mixed with valid ones; node left running past the cert's validity window; NTP failure shifting the clock; CI fixtures with hard-coded dates.

Understand the failure class

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/3e69b7faafa9fe3c. Report an issue: GitHub.