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
- Add the global `pki` option (or any usage of the `internal` issuer) so the PKI app is part of the config.
- If internal PKI is not intended, replace pki_intermediate with `file`, `inline`, `storage`, or `system` trust pools.
- 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
- Treat any `internal` issuer or pki_* trust pool usage as requiring the pki app; add the global pki option in the same change.
- Validate adapted JSON in CI.
- Document in repo README which snippets depend on the internal PKI.
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
- pki_root CA pool requires that a PKI app is configured: %v
- getting CA %s: %v
- CA %s has no root certificate
- --input is required
- setting up subroutes: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/821f4efa4a48a7f7.
Report an issue: GitHub.