caddyserver/caddy · error

CA %s has no root certificate

Error message

CA %s has no root certificate

What it means

During pki_root pool provisioning, ca.RootCertificate() returned nil for a resolved CA, meaning the CA exists but has no root certificate material available yet. The pool cannot trust a CA whose root has not been generated or loaded.

Source

Thrown at modules/caddytls/capools.go:251

	pkiApp, err := ctx.AppIfConfigured("pki")
	if err != nil {
		return fmt.Errorf("pki_root CA pool requires that a PKI app is configured: %v", err)
	}
	pki := pkiApp.(*caddypki.PKI)
	for _, caID := range p.Authority {
		c, err := pki.GetCA(ctx, caID)
		if err != nil || c == nil {
			return fmt.Errorf("getting CA %s: %v", caID, err)
		}
		p.ca = append(p.ca, c)
	}

	caPool := x509.NewCertPool()
	var certs []*x509.Certificate
	for _, ca := range p.ca {
		rootCert := ca.RootCertificate()
		if rootCert == nil {
			return fmt.Errorf("CA %s has no root certificate", ca.ID)
		}
		caPool.AddCert(rootCert)
		certs = append(certs, rootCert)
	}
	p.pool = caPool
	p.certs = certs

	return nil
}

// Syntax:
//
//	trust_pool pki_root [<ca_name>...] {
//		authority <ca_name>...
//	}
//
// The 'authority' directive can be specified multiple times.
func (pkir *PKIRootCAPool) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Inspect the `root` sub-directive of the CA in the pki block: if it points at a cert file, ensure the file exists, is a valid PEM certificate, and is readable by Caddy.
  2. Remove stale/partial internal CA data from storage (e.g. the PKI app's storage keys) so the root is regenerated cleanly, then reload.
  3. If embedding Caddy programmatically, ensure the pki app is fully provisioned before the caddytls app references its CAs.

Example fix

# before
{
  pki {
    ca custom {
      root { cert /etc/caddy/missing-root.crt key /etc/caddy/missing-root.key }
    }
  }
}

# after  (let Caddy manage the root, or point at existing files)
{
  pki {
    ca custom {
      root { cert /etc/caddy/root.crt key /etc/caddy/root.key }
    }
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// when embedding Caddy, confirm the CA has a root before provisioning caddytls
if ca, err := pkiApp.GetCA(ctx, "local"); err == nil && ca != nil {
	if ca.RootCertificate() == nil {
		return fmt.Errorf("CA %s not ready: install or generate its root first", ca.ID)
	}
}

Try / catch

// if you drive provisioning manually, surface the error and stop rather than continuing
if err := pool.Provision(ctx); err != nil {
	log.Printf("trust pool provisioning failed: %v", err)
	return err // do not fall back to an empty pool
}

Prevention

When it happens

Trigger: The PKI app created the CA object lazily via GetCA but the root key/certificate have not been installed (root generation failed or was deferred), or a custom CA was configured with external/root-less settings so no root certificate exists at provision time.

Common situations: A pki CA defined with a missing or unreadable root certificate file path; storage backing the internal CA was wiped or is read-only; interleaving of provisioning order in unusual programmatic embedding of Caddy.

Understand the failure class

Related errors


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