caddyserver/caddy · error
'ca' module '%s' is not a certificate pool provider
Error message
'ca' module '%s' is not a certificate pool provider
What it means
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.
Source
Thrown at modules/caddytls/connpolicy.go:816
}
err := caPool.Provision(ctx)
if err != nil {
return err
}
clientauth.ca = caPool
}
// if we don't have any CARaw set, there's not much work to do
if clientauth.CARaw == nil {
return nil
}
caRaw, err := ctx.LoadModule(clientauth, "CARaw")
if err != nil {
return err
}
ca, ok := caRaw.(CA)
if !ok {
return fmt.Errorf("'ca' module '%s' is not a certificate pool provider", ca)
}
clientauth.ca = ca
return nil
}
// Active returns true if clientauth has an actionable configuration.
func (clientauth ClientAuthentication) Active() bool {
return len(clientauth.TrustedCACerts) > 0 ||
len(clientauth.TrustedCACertPEMFiles) > 0 ||
len(clientauth.TrustedLeafCerts) > 0 || // TODO: DEPRECATED
len(clientauth.VerifiersRaw) > 0 ||
len(clientauth.Mode) > 0 ||
clientauth.CARaw != nil || clientauth.ca != nil
}
// ConfigureTLSConfig sets up cfg to enforce clientauth's configuration.
func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) error {View on GitHub (pinned to 50e54ee279)
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
Defensive patterns
Strategy: type-guard
Validate before calling
// List candidate CA modules and confirm the ID exists before writing config // (shell) caddy list-modules | grep '^tls\.ca\.'
Type guard
// For plugin authors: compile-time guarantee your module satisfies the CA interface
var _ caddytls.CA = (*MyCAPoolProvider)(nil)
func (m *MyCAPoolProvider) CertPool() *x509.CertPool { /* ... */ return nil } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- could not parse leaf certificates loaders: %s
- leaf module '%s' is not a leaf certificate loader
- matcher module '%s' is not a connection matcher
- marshaling %T matcher: %v
- two policies with same match criteria have conflicting clien
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/7148402b52f27b00.
Report an issue: GitHub.