caddyserver/caddy · critical
module ID missing
Error message
module ID missing
What it means
caddy.RegisterModule validates the ModuleInfo returned by a module's CaddyModule() method before storing it. An empty ID means the module never set its identity, making it unusable for lookup, provisioning, or JSON namespacing — so registration panics immediately at init time.
Source
Thrown at modules.go:142
// is usually read from an associated field's struct tag.)
// Because the module's name is given as the key in a
// module map, the name does not have to be given in the
// json.RawMessage.
type ModuleMap map[string]json.RawMessage
// RegisterModule registers a module by receiving a
// plain/empty value of the module. For registration to
// be properly recorded, this should be called in the
// 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)] = modView on GitHub (pinned to 50e54ee279)
Solutions
- Set a fully-qualified ID in CaddyModule(): ID: "http.handlers.myhandler"
- Follow the namespace.category.name convention so config adapters can reference the module
- Keep the New function returning a fresh non-nil instance of the same type
Example fix
// before
func (MyHandler) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{New: func() caddy.Module { return new(MyHandler) }}
}
// after
func (MyHandler) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.myhandler",
New: func() caddy.Module { return new(MyHandler) },
}
} Defensive patterns
Strategy: validation
Prevention
- Always set a fully-qualified ID in CaddyModule()
- Start new modules from a known-good template
- Run caddy list-modules after adding a module to confirm registration
When it happens
Trigger: Implementing CaddyModule() but returning caddy.ModuleInfo{New: func() caddy.Module { return new(T) }} without setting ID; copy-pasting a module type and forgetting to change the ID.
Common situations: Bootstrapping a new module from a template; refactoring module structs; returning a zero-value ModuleInfo accidentally.
Related errors
- missing ModuleInfo.New
- ModuleInfo.New must return a non-nil module instance
- 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/00c3c6ea0c534878.
Report an issue: GitHub.