{"record":{"id":"7148402b52f27b00","repo":"caddyserver/caddy","slug":"ca-module-s-is-not-a-certificate-pool-provide","errorCode":null,"errorMessage":"'ca' module '%s' is not a certificate pool provider","messagePattern":"'ca' module '(.+?)' is not a certificate pool provider","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/connpolicy.go","lineNumber":816,"sourceCode":"\t\t}\n\t\terr := caPool.Provision(ctx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tclientauth.ca = caPool\n\t}\n\n\t// if we don't have any CARaw set, there's not much work to do\n\tif clientauth.CARaw == nil {\n\t\treturn nil\n\t}\n\tcaRaw, err := ctx.LoadModule(clientauth, \"CARaw\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tca, ok := caRaw.(CA)\n\tif !ok {\n\t\treturn fmt.Errorf(\"'ca' module '%s' is not a certificate pool provider\", ca)\n\t}\n\tclientauth.ca = ca\n\n\treturn nil\n}\n\n// Active returns true if clientauth has an actionable configuration.\nfunc (clientauth ClientAuthentication) Active() bool {\n\treturn len(clientauth.TrustedCACerts) > 0 ||\n\t\tlen(clientauth.TrustedCACertPEMFiles) > 0 ||\n\t\tlen(clientauth.TrustedLeafCerts) > 0 || // TODO: DEPRECATED\n\t\tlen(clientauth.VerifiersRaw) > 0 ||\n\t\tlen(clientauth.Mode) > 0 ||\n\t\tclientauth.CARaw != nil || clientauth.ca != nil\n}\n\n// ConfigureTLSConfig sets up cfg to enforce clientauth's configuration.\nfunc (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) error {","sourceCodeStart":798,"sourceCodeEnd":834,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/connpolicy.go#L798-L834","documentation":"ClientAuthentication.provision loads the module named in the 'ca' field of client authentication and asserts it implements the caddytls CA interface (a certificate pool provider). If the loaded module does not implement CertPool(), provisioning fails. Note the message formats the module value with %s, so the detail printed may be unhelpful (the module's String form or %!s(...) artifact) — a known cosmetic quirk of this line.","triggerScenarios":"Setting \"ca\": {\"module\": \"...\"} under client_authentication to a module ID that exists but is not a CA pool provider, or a custom plugin registered under the wrong namespace; typically unreachable with stock modules unless a third-party module is misregistered.","commonSituations":"Third-party XCaddy plugins that register a module under tls.ca without implementing the CA interface; typos in a custom module namespace; upgrading a plugin that changed its implemented interfaces.","solutions":["Set 'ca' to a module that actually provides a certificate pool (e.g. the internal PKI CA: ca internal)","If this is your own XCaddy plugin, make the type implement CertPool() *x509.CertPool and re-check the interface guard var _ CA = (*YourCA)(nil)","Verify the module ID you typed is registered: caddy list-modules | grep tls.ca","If you do not need a module-based CA, drop 'ca' and use trusted_ca_certs_file instead"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// List candidate CA modules and confirm the ID exists before writing config\n// (shell) caddy list-modules | grep '^tls\\.ca\\.'","typeGuard":"// For plugin authors: compile-time guarantee your module satisfies the CA interface\nvar _ caddytls.CA = (*MyCAPoolProvider)(nil)\n\nfunc (m *MyCAPoolProvider) CertPool() *x509.CertPool { /* ... */ return nil }","tryCatchPattern":null,"preventionTips":["Pin plugin versions with xcaddy and rebuild on every Caddy upgrade","Prefer 'ca internal' unless a custom pool provider is genuinely needed","Add interface guards in every custom caddytls plugin"],"tags":["tls","client-auth","module-system","xcaddy"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}