caddyserver/caddy · error
source %T returned nil certificates
Error message
source %T returned nil certificates
What it means
A source inside a combined CA pool implemented CertificateProvider but its Certificates() call returned a nil slice. The combined pool treats nil (as opposed to an empty non-nil slice) as a defective source and refuses to continue provisioning.
Source
Thrown at modules/caddytls/capools.go:878
caPool := x509.NewCertPool()
var allCerts []*x509.Certificate
for _, src := range sources.([]any) {
ca, ok := src.(CA)
if !ok {
return fmt.Errorf("source module is not a CA pool provider")
}
ccp.sources = append(ccp.sources, ca)
certProvider, ok := ca.(CertificateProvider)
if !ok {
return fmt.Errorf("source %T does not implement CertificateProvider (required for combining)", ca)
}
certs := certProvider.Certificates()
if certs == nil {
return fmt.Errorf("source %T returned nil certificates", ca)
}
for _, cert := range certs {
if cert == nil {
return fmt.Errorf("source %T returned a nil certificate", ca)
}
caPool.AddCert(cert)
allCerts = append(allCerts, cert)
}
}
ccp.pool = caPool
ccp.certs = allCerts
return nil
}
// Syntax:
//View on GitHub (pinned to 50e54ee279)
Solutions
- In the custom module, always initialize the certificates slice during Provision even when empty: `certs := []*x509.Certificate{}`.
- Ensure Certificates() uses a pointer receiver over the same state that Provision populated.
- Add a unit test asserting Certificates() is non-nil after Provision.
Example fix
// before
func (m *MyPool) Provision(ctx caddy.Context) error {
m.pool = x509.NewCertPool()
return nil
}
// after
func (m *MyPool) Provision(ctx caddy.Context) error {
m.pool = x509.NewCertPool()
m.certs = []*x509.Certificate{}
return nil
} Defensive patterns
Strategy: validation
Validate before calling
// custom modules: always initialize the certs slice in Provision
func (m *MyPool) Provision(ctx caddy.Context) error {
m.pool = x509.NewCertPool()
m.certs = []*x509.Certificate{} // non-nil, even when empty
return nil
} Prevention
- Unit-test Certificates() for non-nil after Provision in custom pool modules.
- Use pointer receivers consistently so Provision's writes are visible to Certificates().
- Return an empty non-nil slice rather than nil on no-certificate paths.
When it happens
Trigger: A custom source module whose Certificates() returns nil because certificates were not stored during its Provision (e.g. field never populated on an error path, or value-receiver returning an unset field).
Common situations: Custom pool modules that lazily build the pool but forget to populate the certs slice; copy-paste module skeletons; receiver-type mistakes (value receiver on pointer-populated state).
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- source %T does not implement CertificateProvider (required f
- CA %s has a nil certificate in its intermediate chain
- no sources specified for combined CA pool
- loading CA pool sources: %v
- source module is not a CA pool provider
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/7d9b5f8c0feac358.
Report an issue: GitHub.