{"record":{"id":"103325fb8dc517ac","repo":"caddyserver/caddy","slug":"decoded-module-is-not-a-requestmatcher-or-requestm","errorCode":null,"errorMessage":"decoded module is not a RequestMatcher or RequestMatcherWithError: %#v","messagePattern":"decoded module is not a RequestMatcher or RequestMatcherWithError: %#v","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/routes.go","lineNumber":453,"sourceCode":"\t\t}\n\t}\n\treturn len(ms) == 0, nil\n}\n\n// FromInterface fills ms from an 'any' value obtained from LoadModule.\nfunc (ms *MatcherSets) FromInterface(matcherSets any) error {\n\tfor _, matcherSetIfaces := range matcherSets.([]map[string]any) {\n\t\tvar matcherSet MatcherSet\n\t\tfor _, matcher := range matcherSetIfaces {\n\t\t\tif m, ok := matcher.(RequestMatcherWithError); ok {\n\t\t\t\tmatcherSet = append(matcherSet, m)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif m, ok := matcher.(RequestMatcher); ok {\n\t\t\t\tmatcherSet = append(matcherSet, m)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"decoded module is not a RequestMatcher or RequestMatcherWithError: %#v\", matcher)\n\t\t}\n\t\t*ms = append(*ms, matcherSet)\n\t}\n\treturn nil\n}\n\n// TODO: Is this used?\nfunc (ms MatcherSets) String() string {\n\tvar result strings.Builder\n\tresult.WriteByte('[')\n\tfor _, matcherSet := range ms {\n\t\tfor _, matcher := range matcherSet {\n\t\t\tfmt.Fprintf(&result, \" %#v\", matcher)\n\t\t}\n\t}\n\tresult.WriteByte(']')\n\treturn result.String()\n}","sourceCodeStart":435,"sourceCodeEnd":471,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/routes.go#L435-L471","documentation":"After modules decode from JSON, MatcherSets.FromInterface type-asserts each decoded value to RequestMatcher or RequestMatcherWithError. If a module loaded through the matcher namespace implements neither interface, this error is returned with the Go value printed. It signals a module registered under http.matchers.* that is not actually a matcher.","triggerScenarios":"A custom plugin module whose CaddyModule() ID is under the matchers namespace but which does not implement RequestMatcherWithError/RequestMatcher; or a module mis-decoded into the match sets field via hand-crafted JSON.","commonSituations":"Writing a third-party Caddy plugin and getting the module ID namespace or interface implementation wrong; very rare with stock configs.","solutions":["If you develop the plugin: make the type implement caddyhttp.RequestMatcher (Matches(r *http.Request) bool) or RequestMatcherWithError, and add a compile-time interface guard","Verify the module ID namespace matches the module's actual role (http.matchers.* for matchers)","If you only consume the config: remove the invalid module from the match block and report it to the plugin author"],"exampleFix":"// before (plugin)\nfunc (m MyThing) CaddyModule() caddy.ModuleInfo {\n  return caddy.ModuleInfo{ ID: \"http.matchers.mything\", New: func() caddy.Module { return new(MyThing) } }\n}\n// MyThing has no Matches method\n\n// after\nvar _ caddyhttp.RequestMatcherWithError = (*MyThing)(nil)\n\nfunc (m *MyThing) Matches(r *http.Request) error { return nil }","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// plugin-side compile-time guard (prevents shipping a matcher that isn't one):\nvar (\n    _ caddyhttp.RequestMatcherWithError = (*MyMatcher)(nil)\n    _ caddy.Module                      = (*MyMatcher)(nil)\n)\n\n// consumer-side runtime guard over decoded matchers:\nfunc isRequestMatcher(v any) bool {\n    switch v.(type) {\n    case caddyhttp.RequestMatcher, caddyhttp.RequestMatcherWithError:\n        return true\n    }\n    return false\n}","tryCatchPattern":null,"preventionTips":["Always add interface guards in plugin files","Name custom matchers under http.matchers.* and handlers under http.handlers.*","Test plugin configs with 'caddy validate' on an xcaddy build before shipping"],"tags":["caddy","plugin-development","matchers","type-assertion","interfaces"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}