caddyserver/caddy · error

pki_intermediate CA pool requires that a PKI app is configur

Error message

pki_intermediate CA pool requires that a PKI app is configured: %v

What it means

The `tls.ca_pool.source.pki_intermediate` module's Provision() calls ctx.AppIfConfigured("pki") and errors when the PKI app is absent from the effective configuration. This pool trusts the intermediate certificates of Caddy's internal CAs, so the PKI app must be configured first.

Source

Thrown at modules/caddytls/capools.go:320

	pool  *x509.CertPool
	certs []*x509.Certificate
}

// CaddyModule implements caddy.Module.
func (PKIIntermediateCAPool) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID: "tls.ca_pool.source.pki_intermediate",
		New: func() caddy.Module {
			return new(PKIIntermediateCAPool)
		},
	}
}

// Loads the PKI app and loads the intermediate certificates into the certificate pool
func (p *PKIIntermediateCAPool) Provision(ctx caddy.Context) error {
	pkiApp, err := ctx.AppIfConfigured("pki")
	if err != nil {
		return fmt.Errorf("pki_intermediate 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 {
		for _, c := range ca.IntermediateCertificateChain() {
			if c == nil {
				return fmt.Errorf("CA %s has a nil certificate in its intermediate chain", ca.ID)
			}
			caPool.AddCert(c)

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Add the global `pki` option (or any usage of the `internal` issuer) so the PKI app is part of the config.
  2. If internal PKI is not intended, replace pki_intermediate with `file`, `inline`, `storage`, or `system` trust pools.
  3. Validate with `caddy validate --config Caddyfile` before deploy to catch it at load time.

Example fix

# before
example.com {
  tls { client_auth { trust_pool pki_intermediate } }
}

# after
{
  pki
}
example.com {
  tls { client_auth { trust_pool pki_intermediate } }
}
Defensive patterns

Strategy: validation

Validate before calling

// confirm pki app is configured before using pki_intermediate
import "encoding/json"

func hasPKIApp(cfgJSON []byte) bool {
	var cfg struct {
		Apps map[string]json.RawMessage `json:"apps"`
	}
	return json.Unmarshal(cfgJSON, &cfg) == nil
		&& func() bool { _, ok := cfg.Apps["pki"]; return ok }()
}

Prevention

When it happens

Trigger: Using `trust_pool pki_intermediate` in the tls/client_auth config while no pki app block (global `pki` option or any `internal` issuer usage) exists in the same configuration.

Common situations: Adding client-auth with intermediate trust to a site that previously had no internal CA; splitting a config into snippets where the pki global option was left behind; test configs trimmed down to the minimum.

Related errors


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