{"record":{"id":"b891f19ff37b7bde","repo":"caddyserver/caddy","slug":"unrecognized-type-for-module-s","errorCode":null,"errorMessage":"unrecognized type for module: %s","messagePattern":"unrecognized type for module: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"context.go","lineNumber":280,"sourceCode":"\t\t\tfor i := 0; i < val.Len(); i++ {\n\t\t\t\tthisSet, err := ctx.loadModulesFromSomeMap(moduleNamespace, inlineModuleKey, val.Index(i))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tall = append(all, thisSet)\n\t\t\t}\n\t\t\tresult = all\n\t\t}\n\n\tcase reflect.Map:\n\t\t// val is a ModuleMap or some other kind of map\n\t\tresult, err = ctx.loadModulesFromSomeMap(moduleNamespace, inlineModuleKey, val)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unrecognized type for module: %s\", typ)\n\t}\n\n\t// we're done with the raw bytes; allow GC to deallocate\n\tval.Set(reflect.Zero(typ))\n\n\treturn result, nil\n}\n\n// emitEvent is a small convenience method so the caddy core can emit events, if the event app is configured.\nfunc (ctx Context) emitEvent(name string, data map[string]any) Event {\n\tif ctx.cfg == nil || ctx.cfg.eventEmitter == nil {\n\t\treturn Event{}\n\t}\n\treturn ctx.cfg.eventEmitter.Emit(ctx, name, data)\n}\n\n// loadModulesFromSomeMap loads modules from val, which must be a type of map[string]any.\n// Depending on inlineModuleKey, it will be interpreted as either a ModuleMap (key is the module","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/context.go#L262-L298","documentation":"Context.LoadModule decides how to load nested modules based on the reflected type of the destination field. It handles pointers to modules, json.RawMessage slices (flat and nested), ModuleMap types, and maps. If the field's kind matches none of these, it falls through to default and returns 'unrecognized type for module: %s' with the Go type string. This is a developer error: the struct field tagged with a caddy module tag has a type the loader cannot process.","triggerScenarios":"A plugin declares a struct field with a caddy:\"module=...\" (or inline_key) tag whose type is not *SomeModule, ModuleMap, map[string]json.RawMessage, []json.RawMessage, or [][]json.RawMessage — e.g. an int, string, non-module struct, or non-pointer non-map type — and the module gets loaded during provisioning.","commonSituations":"Plugin authors copying module struct patterns and forgetting the field must be a pointer or ModuleMap; tagging a plain non-module field with a caddy module tag by accident; refactoring a field from *SomeModule to SomeModule (non-pointer).","solutions":["Change the struct field type to one the loader supports: *SomeModule (pointer to registered module), caddy.ModuleMap, map[string]json.RawMessage, []json.RawMessage, or [][]json.RawMessage","If the field is not supposed to hold a module, remove the caddy:\"module=...\" struct tag so LoadModule skips it","Rebuild the plugin/binary and re-run config validation"],"exampleFix":"// before\ntype MyHandler struct {\n    Upstream http.Upstream `json:\"upstream\"` // module tag elsewhere, non-pointer value type\n}\n// after\ntype MyHandler struct {\n    Upstream caddyhttp.UpstreamHandler `json:\"upstream\"` // use pointer/module-compatible type\n}","handlingStrategy":"type-guard","validationCode":"// Plugin build-time: assert fields tagged as modules have loadable types\nfunc assertModuleFieldType(t reflect.Type) error {\n    switch {\n    case t.Kind() == reflect.Pointer,\n         isModuleMapType(t),\n         isJSONRawMessage(t):\n        return nil\n    }\n    return fmt.Errorf(\"unrecognized type for module: %s\", t)\n}","typeGuard":"// Only tag fields whose type the loader supports\nfunc isLoadableModuleField(t reflect.Type) bool {\n    if t == reflect.TypeOf(caddy.ModuleMap{}) { return true }\n    if t == reflect.TypeOf(json.RawMessage{}) { return true }\n    if t.Kind() == reflect.Pointer { return true }\n    return false\n}","tryCatchPattern":"if _, err := ctx.LoadModule(fv, raw); err != nil { return fmt.Errorf(\"field %s: %w\", field.Name, err) }","preventionTips":["Use the documented field types (*Module, caddy.ModuleMap, json.RawMessage slices) for module fields","Never add caddy:\"module=...\" tags to plain data fields","Add unit tests that load a minimal JSON config through your module"],"tags":["caddy","module-system","reflection","plugin-development"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}