{"record":{"id":"8e2f5f4c4dc59683","repo":"caddyserver/caddy","slug":"source-t-returned-a-nil-certificate","errorCode":null,"errorMessage":"source %T returned a nil certificate","messagePattern":"source %T returned a nil certificate","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/capools.go","lineNumber":882,"sourceCode":"\tfor _, src := range sources.([]any) {\n\t\tca, ok := src.(CA)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"source module is not a CA pool provider\")\n\t\t}\n\t\tccp.sources = append(ccp.sources, ca)\n\n\t\tcertProvider, ok := ca.(CertificateProvider)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"source %T does not implement CertificateProvider (required for combining)\", ca)\n\t\t}\n\n\t\tcerts := certProvider.Certificates()\n\t\tif certs == nil {\n\t\t\treturn fmt.Errorf(\"source %T returned nil certificates\", ca)\n\t\t}\n\t\tfor _, cert := range certs {\n\t\t\tif cert == nil {\n\t\t\t\treturn fmt.Errorf(\"source %T returned a nil certificate\", ca)\n\t\t\t}\n\t\t\tcaPool.AddCert(cert)\n\t\t\tallCerts = append(allCerts, cert)\n\t\t}\n\t}\n\n\tccp.pool = caPool\n\tccp.certs = allCerts\n\n\treturn nil\n}\n\n// Syntax:\n//\n//\ttrust_pool combined {\n//\t\tsource <module_name> {\n//\t\t\t<module_config>\n//\t\t}","sourceCodeStart":864,"sourceCodeEnd":900,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/capools.go#L864-L900","documentation":"Thrown while provisioning a 'combined' CA trust pool (tls.ca_pool.source.combined): one of the configured source modules returned a slice of certificates that contains a nil *x509.Certificate element. Caddy refuses to add nil certs to the x509.CertPool because a nil certificate cannot be parsed and would silently corrupt trust evaluation. The %T verb prints the offending source module's Go type, which identifies which source is broken.","triggerScenarios":"Provisioning a CombinedCertPool whose SourcesRaw includes a custom CA source module (or a third-party plugin) whose Certificates() implementation returns a slice like []*x509.Certificate{validCert, nil}. Also reachable if a source builds its slice with make([]*x509.Certificate, n) and fails to fill every index, or appends a nil on a parse failure instead of returning an error.","commonSituations":"A plugin implementing the CA/CertificateProvider interfaces with sloppy slice handling; a source that reads a directory of DER files, skips unreadable ones by appending nil, and the operator points it at a partially-readable directory.","solutions":["Read the %T type name in the error to identify which source module is at fault","Fix that module's Certificates() so it never returns nil elements: skip the entry, or return an error explaining why the certificate could not be loaded","Verify the certificate files/inputs feeding that source are valid and readable","If the module is third-party, report the bug upstream and pin a working version"],"exampleFix":"// before (inside a custom source's Certificates())\ncerts := make([]*x509.Certificate, len(files))\nfor i, f := range files {\n\tder, _ := os.ReadFile(f) // error ignored\n\tcert, _ := x509.ParseCertificate(der)\n\tcerts[i] = cert // can be nil\n}\nreturn certs\n\n// after\nvar certs []*x509.Certificate\nfor _, f := range files {\n\tder, err := os.ReadFile(f)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"reading %s: %v\", f, err)\n\t}\n\tcert, err := x509.ParseCertificate(der)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing %s: %v\", f, err)\n\t}\n\tcerts = append(certs, cert)\n}\nreturn certs","handlingStrategy":"validation","validationCode":"// Before returning certs from a custom CA source, self-check:\nfunc validateCerts(certs []*x509.Certificate) error {\n\tfor i, c := range certs {\n\t\tif c == nil {\n\t\t\treturn fmt.Errorf(\"certificate at index %d is nil\", i)\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When implementing CertificateProvider, never append nil on parse failure — return an error instead","Avoid make([]*x509.Certificate, n) + index assignment; use append so unfilled slots cannot exist","Test custom CA sources with an empty and a partially-invalid input directory before shipping","Keep combined trust pool configs small so the %T in the error is easy to map to one source"],"tags":["caddy","tls","x509","trust-pool","plugin"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}