caddyserver/caddy · critical
ModuleInfo.New must return a non-nil module instance
Error message
ModuleInfo.New must return a non-nil module instance
What it means
RegisterModule smoke-tests the factory by calling New() once; if it returns nil, registration panics. A New that returns a nil interface (often a typed nil pointer or an early return) would break every later instantiation path, so Caddy rejects it up front.
Source
Thrown at modules.go:151
// init phase of runtime. Typically, the module package
// will do this as a side-effect of being imported.
// This function panics if the module's info is
// incomplete or invalid, or if the module is already
// registered.
func RegisterModule(instance Module) {
mod := instance.CaddyModule()
if mod.ID == "" {
panic("module ID missing")
}
if mod.ID == "caddy" || mod.ID == "admin" {
panic(fmt.Sprintf("module ID '%s' is reserved", mod.ID))
}
if mod.New == nil {
panic("missing ModuleInfo.New")
}
if val := mod.New(); val == nil {
panic("ModuleInfo.New must return a non-nil module instance")
}
modulesMu.Lock()
defer modulesMu.Unlock()
if _, ok := modules[string(mod.ID)]; ok {
panic(fmt.Sprintf("module already registered: %s", mod.ID))
}
modules[string(mod.ID)] = mod
}
// GetModule returns module information from its ID (full name).
func GetModule(name string) (ModuleInfo, error) {
modulesMu.RLock()
defer modulesMu.RUnlock()
m, ok := modules[name]
if !ok {
return ModuleInfo{}, fmt.Errorf("module not registered: %s", name)View on GitHub (pinned to 50e54ee279)
Solutions
- Make New unconditionally return a new instance: func() caddy.Module { return new(T) }
- Do not put environment/config-dependent logic in New; move it to Provision()
- If using generics or wrappers, verify the returned value is non-nil before returning it
Example fix
// before
New: func() caddy.Module { var h *MyHandler; return h },
// after
New: func() caddy.Module { return new(MyHandler) }, Defensive patterns
Strategy: validation
Prevention
- New must unconditionally return a fresh non-nil instance
- Move conditional logic to Provision()
- Avoid returning typed nil pointers from New
When it happens
Trigger: New returning nil explicitly, returning a nil *T stored in the caddy.Module interface (typed nil is non-nil interface but a `return nil` is caught), or logic like `if disabled { return nil }`.
Common situations: Conditional factories; refactors that return a nil pointer from a helper; returning a nil interface from a switch over config (though config isn't available at init — the panic still fires if the closure unconditionally returns nil).
Related errors
- module ID missing
- missing ModuleInfo.New
- malformed tag on field %s: %v
- missing 'namespace' key in struct tag on field %s
- unable to determine module name without inline_key when type
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/abe021e96ee00528.
Report an issue: GitHub.