{"record":{"id":"9bfe3414f6318ad3","repo":"caddyserver/caddy","slug":"missing-moduleinfo-new","errorCode":null,"errorMessage":"missing ModuleInfo.New","messagePattern":"missing ModuleInfo\\.New","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"modules.go","lineNumber":148,"sourceCode":"// 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\n}\n\n// GetModule returns module information from its ID (full name).\nfunc GetModule(name string) (ModuleInfo, error) {\n\tmodulesMu.RLock()\n\tdefer modulesMu.RUnlock()","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules.go#L130-L166","documentation":"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.","triggerScenarios":"Returning a ModuleInfo with ID set but no New function: caddy.ModuleInfo{ID: \"http.handlers.x\"}.","commonSituations":"Hand-writing a module skeleton and filling in ID first; deleting the New closure during refactoring; interface-guard copies that drop fields.","solutions":["Add New: func() caddy.Module { return new(YourType) } to the ModuleInfo","Ensure New returns a pointer or value of your module type each call (fresh instance)","Keep the compile-time guard var _ caddy.Module = (*YourType)(nil) so signature drift is caught"],"exampleFix":"// before\nreturn caddy.ModuleInfo{ID: \"http.handlers.x\"}\n// after\nreturn caddy.ModuleInfo{\n    ID:  \"http.handlers.x\",\n    New: func() caddy.Module { return new(XHandler) },\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["ModuleInfo must contain both ID and New","Keep the interface guard var _ caddy.Module = (*T)(nil)","Test that a config referencing the module round-trips"],"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"}