{"record":{"id":"0ed224604852da62","repo":"caddyserver/caddy","slug":"ca-s-has-no-root-certificate","errorCode":null,"errorMessage":"CA %s has no root certificate","messagePattern":"CA (.+?) has no root certificate","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/capools.go","lineNumber":251,"sourceCode":"\tpkiApp, err := ctx.AppIfConfigured(\"pki\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"pki_root CA pool requires that a PKI app is configured: %v\", err)\n\t}\n\tpki := pkiApp.(*caddypki.PKI)\n\tfor _, caID := range p.Authority {\n\t\tc, err := pki.GetCA(ctx, caID)\n\t\tif err != nil || c == nil {\n\t\t\treturn fmt.Errorf(\"getting CA %s: %v\", caID, err)\n\t\t}\n\t\tp.ca = append(p.ca, c)\n\t}\n\n\tcaPool := x509.NewCertPool()\n\tvar certs []*x509.Certificate\n\tfor _, ca := range p.ca {\n\t\trootCert := ca.RootCertificate()\n\t\tif rootCert == nil {\n\t\t\treturn fmt.Errorf(\"CA %s has no root certificate\", ca.ID)\n\t\t}\n\t\tcaPool.AddCert(rootCert)\n\t\tcerts = append(certs, rootCert)\n\t}\n\tp.pool = caPool\n\tp.certs = certs\n\n\treturn nil\n}\n\n// Syntax:\n//\n//\ttrust_pool pki_root [<ca_name>...] {\n//\t\tauthority <ca_name>...\n//\t}\n//\n// The 'authority' directive can be specified multiple times.\nfunc (pkir *PKIRootCAPool) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/capools.go#L233-L269","documentation":"During pki_root pool provisioning, ca.RootCertificate() returned nil for a resolved CA, meaning the CA exists but has no root certificate material available yet. The pool cannot trust a CA whose root has not been generated or loaded.","triggerScenarios":"The PKI app created the CA object lazily via GetCA but the root key/certificate have not been installed (root generation failed or was deferred), or a custom CA was configured with external/root-less settings so no root certificate exists at provision time.","commonSituations":"A pki CA defined with a missing or unreadable root certificate file path; storage backing the internal CA was wiped or is read-only; interleaving of provisioning order in unusual programmatic embedding of Caddy.","solutions":["Inspect the `root` sub-directive of the CA in the pki block: if it points at a cert file, ensure the file exists, is a valid PEM certificate, and is readable by Caddy.","Remove stale/partial internal CA data from storage (e.g. the PKI app's storage keys) so the root is regenerated cleanly, then reload.","If embedding Caddy programmatically, ensure the pki app is fully provisioned before the caddytls app references its CAs."],"exampleFix":"# before\n{\n  pki {\n    ca custom {\n      root { cert /etc/caddy/missing-root.crt key /etc/caddy/missing-root.key }\n    }\n  }\n}\n\n# after  (let Caddy manage the root, or point at existing files)\n{\n  pki {\n    ca custom {\n      root { cert /etc/caddy/root.crt key /etc/caddy/root.key }\n    }\n  }\n}","handlingStrategy":"validation","validationCode":"// when embedding Caddy, confirm the CA has a root before provisioning caddytls\nif ca, err := pkiApp.GetCA(ctx, \"local\"); err == nil && ca != nil {\n\tif ca.RootCertificate() == nil {\n\t\treturn fmt.Errorf(\"CA %s not ready: install or generate its root first\", ca.ID)\n\t}\n}","typeGuard":null,"tryCatchPattern":"// if you drive provisioning manually, surface the error and stop rather than continuing\nif err := pool.Provision(ctx); err != nil {\n\tlog.Printf(\"trust pool provisioning failed: %v\", err)\n\treturn err // do not fall back to an empty pool\n}","preventionTips":["When supplying custom root cert files, ship both cert and key files together and verify readability before starting Caddy.","Back up the internal CA storage so roots are never partially lost.","Test config changes in staging where the internal CA state mirrors production."],"tags":["caddy","caddytls","pki","certificate","provisioning"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}