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

  1. Make New unconditionally return a new instance: func() caddy.Module { return new(T) }
  2. Do not put environment/config-dependent logic in New; move it to Provision()
  3. 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

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


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