caddyserver/caddy · error
error loading cert '%s' from storage: %s
Error message
error loading cert '%s' from storage: %s
What it means
A storage-backed trust pool could not Load() one of the configured PEM keys from its storage backend. The wrapped error indicates whether the key was not found, was unreadable, or the backend failed.
Source
Thrown at modules/caddytls/capools.go:429
}
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" {
continue
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return fmt.Errorf("parsing certificate '%s': %v", caID, err)
}
caPool.AddCert(cert)
certs = append(certs, cert)View on GitHub (pinned to 50e54ee279)
Solutions
- Verify each configured key exists in the actual storage backend being used (note: with a custom storage block, paths are resolved by that backend, not the local filesystem).
- Upload/copy the PEM bundles to the storage keys referenced by the pool.
- Check the wrapped error message for the backend-specific cause (not found vs. connection refused vs. permission denied) and fix accordingly.
Example fix
# before
trust_pool storage {
trusted_ca_certs_pem ca-roots.pem # not present in storage root
}
# after
# ensure the object exists: e.g. file_system storage root /var/lib/caddy
# /var/lib/caddy/ca-roots.pem
trust_pool storage {
trusted_ca_certs_pem ca-roots.pem
} Defensive patterns
Strategy: validation
Validate before calling
// pre-check keys exist in a file_system-backed storage before reload
import "os"
func keysExist(root string, keys []string) error {
for _, k := range keys {
if _, err := os.Stat(filepath.Join(root, k)); err != nil {
return fmt.Errorf("missing storage key %s: %w", k, err)
}
}
return nil
} Try / catch
// reload pipeline: fail the deploy on load errors instead of leaving config half-applied
if err := applyConfig(newCfg); err != nil {
log.Printf("config apply failed (keeping previous config): %v", err)
return err
} Prevention
- Upload certificate bundles to storage before the config referencing them is applied.
- Use absolute, backend-relative keys deliberately; remember the custom storage block changes what paths mean.
- In multi-node setups, verify the bundle exists in the shared backend from every node.
When it happens
Trigger: ca.storage.Load(ctx, caID) returning an error — the PEM file/blob does not exist in the configured storage, permissions deny reads, or the remote storage backend errored.
Common situations: Pointing trusted_ca_certs_pem at paths relative to a different storage root; certificates never uploaded to shared storage in multi-node setups; storage bucket/prefix misconfiguration; file permissions.
Related errors
- WebSocket connections aren't allowed.
- Disabling same-origin restrictions is not allowed.
- Buggy browser is sending null Origin header.
- syntax error: unexpected token '%s', expecting '%s', at %s:%
- --input is required
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/fd9e863a4e882819.
Report an issue: GitHub.