{"record":{"id":"00c3c6ea0c534878","repo":"caddyserver/caddy","slug":"module-id-missing","errorCode":null,"errorMessage":"module ID missing","messagePattern":"module ID missing","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"modules.go","lineNumber":142,"sourceCode":"// is usually read from an associated field's struct tag.)\n// Because the module's name is given as the key in a\n// module map, the name does not have to be given in the\n// json.RawMessage.\ntype ModuleMap map[string]json.RawMessage\n\n// RegisterModule registers a module by receiving a\n// plain/empty value of the module. For registration to\n// be properly recorded, this should be called in the\n// init phase of runtime. Typically, the module package\n// will do this as a side-effect of being imported.\n// This function panics if the module's info is\n// incomplete or invalid, or if the module is already\n// registered.\nfunc RegisterModule(instance Module) {\n\tmod := instance.CaddyModule()\n\n\tif mod.ID == \"\" {\n\t\tpanic(\"module ID missing\")\n\t}\n\tif mod.ID == \"caddy\" || mod.ID == \"admin\" {\n\t\tpanic(fmt.Sprintf(\"module ID '%s' is reserved\", mod.ID))\n\t}\n\tif mod.New == nil {\n\t\tpanic(\"missing ModuleInfo.New\")\n\t}\n\tif val := mod.New(); val == nil {\n\t\tpanic(\"ModuleInfo.New must return a non-nil module instance\")\n\t}\n\n\tmodulesMu.Lock()\n\tdefer modulesMu.Unlock()\n\n\tif _, ok := modules[string(mod.ID)]; ok {\n\t\tpanic(fmt.Sprintf(\"module already registered: %s\", mod.ID))\n\t}\n\tmodules[string(mod.ID)] = mod","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules.go#L124-L160","documentation":"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.","triggerScenarios":"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.","commonSituations":"Bootstrapping a new module from a template; refactoring module structs; returning a zero-value ModuleInfo accidentally.","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"],"exampleFix":"// before\nfunc (MyHandler) CaddyModule() caddy.ModuleInfo {\n    return caddy.ModuleInfo{New: func() caddy.Module { return new(MyHandler) }}\n}\n// after\nfunc (MyHandler) CaddyModule() caddy.ModuleInfo {\n    return caddy.ModuleInfo{\n        ID:  \"http.handlers.myhandler\",\n        New: func() caddy.Module { return new(MyHandler) },\n    }\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["caddy","go","module-system","registration","panic"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}