{"record":{"id":"8d3cef09a5f32a79","repo":"caddyserver/caddy","slug":"reading-s-v","errorCode":null,"errorMessage":"reading %s: %v","messagePattern":"reading (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/capools.go","lineNumber":153,"sourceCode":"\n// CaddyModule implements caddy.Module.\nfunc (FileCAPool) CaddyModule() caddy.ModuleInfo {\n\treturn caddy.ModuleInfo{\n\t\tID: \"tls.ca_pool.source.file\",\n\t\tNew: func() caddy.Module {\n\t\t\treturn new(FileCAPool)\n\t\t},\n\t}\n}\n\n// Loads and decodes the DER and pem files to generate the certificate pool\nfunc (f *FileCAPool) Provision(ctx caddy.Context) error {\n\tcaPool := x509.NewCertPool()\n\tvar certs []*x509.Certificate\n\tfor _, pemFile := range f.TrustedCACertPEMFiles {\n\t\tpemContents, err := os.ReadFile(pemFile)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"reading %s: %v\", pemFile, err)\n\t\t}\n\t\t// Parse PEM to extract certificates\n\t\tfor len(pemContents) > 0 {\n\t\t\tvar block *pem.Block\n\t\t\tblock, pemContents = pem.Decode(pemContents)\n\t\t\tif block == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif block.Type != \"CERTIFICATE\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcert, err := x509.ParseCertificate(block.Bytes)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"parsing certificate in %s: %v\", pemFile, err)\n\t\t\t}\n\t\t\tcaPool.AddCert(cert)\n\t\t\tcerts = append(certs, cert)\n\t\t}","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/capools.go#L135-L171","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"# before\nsudo chmod 600 /etc/caddy/ca.pem  # caddy cannot read\nclient_auth {\n\ttrusted_ca_cert_file /etc/caddy/ca.pem\n}\n\n# after\nsudo chmod 644 /etc/caddy/ca.pem\nclient_auth {\n\ttrusted_ca_cert_file /etc/caddy/ca.pem\n}","handlingStrategy":"validation","validationCode":"for _, f := range poolCfg.TrustedCACertPEMFiles {\n    info, err := os.Stat(f)\n    if err != nil {\n        return fmt.Errorf(\"CA file %s: %v\", f, err)\n    }\n    if info.IsDir() || info.Mode().Perm()&0o004 == 0 {\n        return fmt.Errorf(\"CA file %s must be a readable regular file\", f)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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)."],"tags":["tls","client-auth","filesystem","permissions","configuration"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}