caddyserver/caddy · error
no PEM keys specified
Error message
no PEM keys specified
What it means
StoragePool.Provision rejects a `tls.ca_pool.source.storage` trust pool whose PEMKeys list is empty. The storage pool reads certificate PEM files from a storage backend, so at least one storage key must be named.
Source
Thrown at modules/caddytls/capools.go:422
// Provision implements caddy.Provisioner.
func (ca *StoragePool) Provision(ctx caddy.Context) error {
if ca.StorageRaw != nil {
val, err := ctx.LoadModule(ca, "StorageRaw")
if err != nil {
return fmt.Errorf("loading storage module: %v", err)
}
cmStorage, err := val.(caddy.StorageConverter).CertMagicStorage()
if err != nil {
return fmt.Errorf("creating storage configuration: %v", err)
}
ca.storage = cmStorage
}
if ca.storage == nil {
ca.storage = ctx.Storage()
}
if len(ca.PEMKeys) == 0 {
return fmt.Errorf("no PEM keys specified")
}
caPool := x509.NewCertPool()
var certs []*x509.Certificate
for _, caID := range ca.PEMKeys {
bs, err := ca.storage.Load(ctx, caID)
if err != nil {
return fmt.Errorf("error loading cert '%s' from storage: %s", caID, err)
}
// Parse PEM to extract certificates
pemData := bs
for len(pemData) > 0 {
var block *pem.Block
block, pemData = pem.Decode(pemData)
if block == nil {
break
}
if block.Type != "CERTIFICATE" {
continueView on GitHub (pinned to 50e54ee279)
Solutions
- Add at least one storage key / PEM path argument to the trust_pool storage block.
- If you intended no storage-based trust, remove the trust_pool storage block entirely.
- Validate config with `caddy validate` to catch empty blocks before reload.
Example fix
# before
trust_pool storage {
trusted_ca_certs_pem
}
# after
trust_pool storage {
trusted_ca_certs_pem certs/internal-ca-root.pem
} Defensive patterns
Strategy: validation
Validate before calling
// for generated configs: skip emitting the block when empty
if len(pemKeys) == 0 {
return nil // do not add a trust_pool storage source
} Prevention
- Always run `caddy validate` on templated configs before applying.
- Treat empty trust pool blocks as lint errors in config reviews.
- When snippets parameterize PEM lists, default them to a required value rather than empty.
When it happens
Trigger: Configuring `trust_pool storage` with no `trusted_ca_certs_pem`/PEM key arguments (or an empty list after Caddyfile parsing / JSON with an empty array).
Common situations: Placeholder or half-written config blocks; snippets where the PEM list is templated to empty; JSON configs where the pem_keys array was dropped during refactoring.
Related errors
- loading storage module: %v
- creating storage configuration: %v
- invalid TLS renegotiation level: %v
- no sources specified for combined CA pool
- WebSocket connections aren't allowed.
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/aefd16af0e42732a.
Report an issue: GitHub.