{"record":{"id":"8dfe8144e5395912","repo":"caddyserver/caddy","slug":"decoding-module-config-s-v","errorCode":null,"errorMessage":"decoding module config: %s: %v","messagePattern":"decoding module config: (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"context.go","lineNumber":392,"sourceCode":"\t}\n\n\tval := modInfo.New()\n\n\t// value must be a pointer for unmarshaling into concrete type, even if\n\t// the module's concrete type is a slice or map; New() *should* return\n\t// a pointer, otherwise unmarshaling errors or panics will occur\n\tif rv := reflect.ValueOf(val); rv.Kind() != reflect.Pointer {\n\t\tlog.Printf(\"[WARNING] ModuleInfo.New() for module '%s' did not return a pointer,\"+\n\t\t\t\" so we are using reflection to make a pointer instead; please fix this by\"+\n\t\t\t\" using new(Type) or &Type notation in your module's New() function.\", id)\n\t\tval = reflect.New(rv.Type()).Elem().Addr().Interface().(Module)\n\t}\n\n\t// fill in its config only if there is a config to fill in\n\tif len(rawMsg) > 0 {\n\t\terr := StrictUnmarshalJSON(rawMsg, &val)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decoding module config: %s: %v\", modInfo, err)\n\t\t}\n\t}\n\n\tif val == nil {\n\t\t// returned module values are almost always type-asserted\n\t\t// before being used, so a nil value would panic; and there\n\t\t// is no good reason to explicitly declare null modules in\n\t\t// a config; it might be because the user is trying to achieve\n\t\t// a result the developer isn't expecting, which is a smell\n\t\treturn nil, fmt.Errorf(\"module value cannot be null\")\n\t}\n\n\tvar err error\n\n\t// if this is an app module, keep a reference to it,\n\t// since submodules may need to reference it during\n\t// provisioning (even though the parent app module\n\t// may not be fully provisioned yet; this is the case","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/context.go#L374-L410","documentation":"After instantiating a module, LoadModuleByID decodes the user's JSON into it with caddy.StrictUnmarshalJSON, which uses DisallowUnknownFields. Any JSON that does not match the module's struct — unknown keys, wrong value types, malformed syntax — produces 'decoding module config: %s: %v' where %s is the ModuleInfo and %v is the encoding/json error including the offending field path.","triggerScenarios":"Supplying a config object with a field the module struct does not define (e.g. \"body\" vs \"contents\"), a string where a duration/array is expected, or trailing syntax errors; StrictUnmarshalJSON rejects it and LoadModuleByID wraps the failure with the module identity.","commonSituations":"Hand-written JSON using guessed field names; configs migrated from older Caddy versions where fields were renamed (respond \"body\" → \"body\" vs older \"contents\"); forgetting that durations must be strings like \"30s\"; copying examples for a different module version.","solutions":["Read the tail of the message: encoding/json errors name the unknown/invalid field (e.g. 'unknown field \"responce\"')","Check the module's documented JSON schema (https://caddyserver.com/docs/json/ or module docs) and correct the field name/type","Remove custom/legacy fields that no longer exist in the module version you run","Prefer adapting from Caddyfile (`caddy adapt`) so field names are generated correctly, then validate with `caddy validate`"],"exampleFix":"// before\n{ \"handler\": \"static_response\", \"responce\": \"hi\" }\n// after\n{ \"handler\": \"static_response\", \"body\": \"hi\" }","handlingStrategy":"validation","validationCode":"// Pre-flight with the same strict decoder Caddy uses\nif err := json.Unmarshal(raw, &target); err != nil {\n    // catches syntax/type errors early; strict unknown-field check:\n    dec := json.NewDecoder(bytes.NewReader(raw))\n    dec.DisallowUnknownFields()\n    if err := dec.Decode(&target); err != nil { return err }\n}","typeGuard":null,"tryCatchPattern":"if _, err := ctx.LoadModuleByID(id, raw); err != nil {\n    return fmt.Errorf(\"config for %s rejected: %w\", id, err) // inner error names the unknown field\n}","preventionTips":["Use `caddy adapt` to generate JSON with correct field names","Check module docs for field names/types before hand-editing JSON","Encode durations as strings (\"30s\"), arrays as arrays"],"tags":["caddy","module-system","json","config","strict-decoding"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}