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

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Set a fully-qualified ID in CaddyModule(): ID: "http.handlers.myhandler"
  2. Follow the namespace.category.name convention so config adapters can reference the module
  3. 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

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


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