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
- Replace the expired CA/leaf certificate with one whose NotAfter is in the future.
- Check pool.IsExpired / the combined pool+ErrExpired result: the pool is still usable if other CAs are valid; filter or renew expired entries.
- Fix clock/NTP sync on the host.
- 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
- Treat NewCAPoolFromBytes' pool+error combo as partially valid — don't discard the pool when only some CAs expired.
- Schedule certificate renewal before NotAfter.
- Check host clock/NTP when expiry seems premature.
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/3e69b7faafa9fe3c.
Report an issue: GitHub.