caddyserver/caddy · error
reading %s: %v
Error message
reading %s: %v
What it means
FileCAPool.Provision reads each file listed in trusted_ca_certs_pem_files with os.ReadFile; when the OS returns an error the wrapper reports the path and cause. This is a local filesystem problem before any parsing happens: the file does not exist, the caddy process lacks read permission, or the path is a directory.
Source
Thrown at modules/caddytls/capools.go:153
// CaddyModule implements caddy.Module.
func (FileCAPool) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "tls.ca_pool.source.file",
New: func() caddy.Module {
return new(FileCAPool)
},
}
}
// Loads and decodes the DER and pem files to generate the certificate pool
func (f *FileCAPool) Provision(ctx caddy.Context) error {
caPool := x509.NewCertPool()
var certs []*x509.Certificate
for _, pemFile := range f.TrustedCACertPEMFiles {
pemContents, err := os.ReadFile(pemFile)
if err != nil {
return fmt.Errorf("reading %s: %v", pemFile, err)
}
// Parse PEM to extract certificates
for len(pemContents) > 0 {
var block *pem.Block
block, pemContents = pem.Decode(pemContents)
if block == nil {
break
}
if block.Type != "CERTIFICATE" {
continue
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return fmt.Errorf("parsing certificate in %s: %v", pemFile, err)
}
caPool.AddCert(cert)
certs = append(certs, cert)
}View on GitHub (pinned to 50e54ee279)
Solutions
- Confirm the file exists at the exact path in the error (ls -l), checking for typos.
- Grant read access: chmod o+r ca.pem or chown to the caddy user; for systemd, ensure the file is readable by User=caddy.
- Use absolute paths, and in containers verify the volume mount actually places the file at that path.
- If the message says 'is a directory', point to the .pem file itself, not its folder.
Example fix
# before
sudo chmod 600 /etc/caddy/ca.pem # caddy cannot read
client_auth {
trusted_ca_cert_file /etc/caddy/ca.pem
}
# after
sudo chmod 644 /etc/caddy/ca.pem
client_auth {
trusted_ca_cert_file /etc/caddy/ca.pem
} Defensive patterns
Strategy: validation
Validate before calling
for _, f := range poolCfg.TrustedCACertPEMFiles {
info, err := os.Stat(f)
if err != nil {
return fmt.Errorf("CA file %s: %v", f, err)
}
if info.IsDir() || info.Mode().Perm()&0o004 == 0 {
return fmt.Errorf("CA file %s must be a readable regular file", f)
}
} Prevention
- Use absolute paths in production configs.
- Mount CA files into containers read-only at fixed paths and validate in entrypoint.
- Make file readability part of pre-deploy checks (run as the caddy service user).
When it happens
Trigger: Pointing trusted_ca_certs_pem_files (Caddyfile: trusted_ca_cert_file) at a nonexistent or misspelled path, a path not readable by the caddy service user, or a relative path resolved against a different working directory (notably with systemd or containers).
Common situations: Certificates stored under /root readable only by root while Caddy runs as caddy; Docker deployments where the CA file was not mounted into the container; paths with typos or missing directory components.
Related errors
- reading default Caddyfile: %v
- loading trusted root CA's PEM file: %s: %v
- parsing certificate at index %d: %v
- loading client cert verifiers: %v
- provisioning client CA: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/8d3cef09a5f32a79.
Report an issue: GitHub.