caddyserver/caddy · critical

missing ModuleInfo.New

Error message

missing ModuleInfo.New

What it means

ModuleInfo.New is the factory Caddy calls to instantiate a module during config unmarshal and provisioning. If New is nil, Caddy could never create instances, so RegisterModule panics at init time. This catches an incomplete CaddyModule() implementation before it can cause nil-pointer chaos later.

Source

Thrown at modules.go:148

// 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)] = mod
}

// GetModule returns module information from its ID (full name).
func GetModule(name string) (ModuleInfo, error) {
	modulesMu.RLock()
	defer modulesMu.RUnlock()

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Add New: func() caddy.Module { return new(YourType) } to the ModuleInfo
  2. Ensure New returns a pointer or value of your module type each call (fresh instance)
  3. Keep the compile-time guard var _ caddy.Module = (*YourType)(nil) so signature drift is caught

Example fix

// before
return caddy.ModuleInfo{ID: "http.handlers.x"}
// after
return caddy.ModuleInfo{
    ID:  "http.handlers.x",
    New: func() caddy.Module { return new(XHandler) },
}
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Returning a ModuleInfo with ID set but no New function: caddy.ModuleInfo{ID: "http.handlers.x"}.

Common situations: Hand-writing a module skeleton and filling in ID first; deleting the New closure during refactoring; interface-guard copies that drop fields.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/9bfe3414f6318ad3. Report an issue: GitHub.