caddyserver/caddy · critical

parsing root certificate PEM: %v

Error message

parsing root certificate PEM: %v

What it means

A root certificate PEM was loaded from storage (so storage is healthy) but pemDecodeCertificate could not parse it into an x509.Certificate. This means the stored root.crt bytes are not a valid PEM CERTIFICATE block or not a parseable certificate — i.e. the asset is corrupt or was overwritten with different content.

Source

Thrown at modules/caddypki/ca.go:301

		return rootChain[0], rootSigner, nil
	}
	rootCertPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyRootCert())
	if err != nil {
		if !errors.Is(err, fs.ErrNotExist) {
			return nil, nil, fmt.Errorf("loading root cert: %v", err)
		}

		// TODO: should we require that all or none of the assets are required before overwriting anything?
		rootCert, rootKey, err = ca.genRoot()
		if err != nil {
			return nil, nil, fmt.Errorf("generating root: %v", err)
		}
	}

	if rootCert == nil {
		rootCert, err = pemDecodeCertificate(rootCertPEM)
		if err != nil {
			return nil, nil, fmt.Errorf("parsing root certificate PEM: %v", err)
		}
	}
	if rootKey == nil {
		rootKeyPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyRootKey())
		if err != nil {
			return nil, nil, fmt.Errorf("loading root key: %v", err)
		}
		rootKey, err = certmagic.PEMDecodePrivateKey(rootKeyPEM)
		if err != nil {
			return nil, nil, fmt.Errorf("decoding root key: %v", err)
		}
	}

	return rootCert, rootKey, nil
}

func (ca CA) genRoot() (rootCert *x509.Certificate, rootKey crypto.Signer, err error) {
	repl := ca.newReplacer()

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Inspect the stored root cert (openssl x509 -in root.crt -noout) to confirm corruption; then restore a known-good copy or delete the CA directory in storage so Caddy regenerates root+intermediate.
  2. If regenerated, re-install trust: caddy untrust && caddy trust (or redistribute the new root to clients) and restart clients relying on old certs.
  3. Check disk space/storage health so Store() cannot half-write assets again.
  4. Avoid hand-editing storage; mount storage read-only to humans if unintended edits recur.
Defensive patterns

Strategy: validation

Validate before calling

// Validate stored root before Caddy starts (cron/pre-start hook)
pem, err := os.ReadFile(rootCertPath)
if err == nil {
    if _, err := pemDecodeOneCert(pem); err != nil {
        log.Fatalf("stored root cert corrupt: %v", err)
    }
}

Type guard

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

Try / catch

if err != nil {
    if strings.Contains(err.Error(), "parsing root certificate PEM") {
        // quarantine CA dir, restore backup or allow regeneration, restart
    }
    return err
}

Prevention

When it happens

Trigger: storage.Load returns bytes that fail pem/x509 parsing: truncated file, PEM block of the wrong type (e.g. a key stored in root.crt), base64 corruption, or a text editor inserting CRLF/BOM. Occurs on any CA load where the root already exists (second start, reload, new authority for an internal issuer).

Common situations: Manual edits to storage/caddy/pki/<id>/ca/root.crt; failed writes (disk full during a previous store); scripts copying the wrong file over root.crt; storage replication that mangled the object.

Understand the failure class

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/85a612fb266c1fcd. Report an issue: GitHub.