{"record":{"id":"8ca09ead20a15b06","repo":"caddyserver/caddy","slug":"module-is-not-a-request-matcher-t","errorCode":null,"errorMessage":"module is not a request matcher: %T","messagePattern":"module is not a request matcher: %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddyhttp/matchers.go","lineNumber":1535,"sourceCode":"\n// Provision loads the matcher modules to be negated.\nfunc (m *MatchNot) Provision(ctx caddy.Context) error {\n\tmatcherSets, err := ctx.LoadModule(m, \"MatcherSetsRaw\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"loading matcher sets: %v\", err)\n\t}\n\tfor _, modMap := range matcherSets.([]map[string]any) {\n\t\tvar ms MatcherSet\n\t\tfor _, modIface := range modMap {\n\t\t\tif mod, ok := modIface.(RequestMatcherWithError); ok {\n\t\t\t\tms = append(ms, mod)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif mod, ok := modIface.(RequestMatcher); ok {\n\t\t\t\tms = append(ms, mod)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"module is not a request matcher: %T\", modIface)\n\t\t}\n\t\tm.MatcherSets = append(m.MatcherSets, ms)\n\t}\n\treturn nil\n}\n\n// Match returns true if r matches m. Since this matcher negates\n// the embedded matchers, false is returned if any of its matcher\n// sets return true.\nfunc (m MatchNot) Match(r *http.Request) bool {\n\tmatch, _ := m.MatchWithError(r)\n\treturn match\n}\n\n// MatchWithError returns true if r matches m. Since this matcher\n// negates the embedded matchers, false is returned if any of its\n// matcher sets return true.\nfunc (m MatchNot) MatchWithError(r *http.Request) (bool, error) {","sourceCodeStart":1517,"sourceCodeEnd":1553,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddyhttp/matchers.go#L1517-L1553","documentation":"After loading the modules nested under a `not` matcher, MatchNot.Provision type-asserts each one to RequestMatcherWithError or RequestMatcher. A module that implements neither is rejected with its Go type in the message — the `not` machinery can only negate actual request matchers.","triggerScenarios":"A custom module registered in a matcher namespace (e.g. http.matchers.foo) whose type implements only caddy.Provisioner or is a handler; placing a non-matcher module inside a `not` block in JSON config.","commonSituations":"Plugin authors putting their module in the wrong namespace; forks where a matcher type changed and lost its Match method; JSON hand-edits that put an interceptor/handler module into a matcher set.","solutions":["Make the module implement caddyhttp.RequestMatcher (Match(*http.Request) bool) or RequestMatcherWithError, and register it under http.matchers.*.","If it is a handler, reference it in the route chain, not inside `not`.","Verify interface compliance at compile time: var _ caddyhttp.RequestMatcher = (*MyMatcher)(nil)."],"exampleFix":"// before (plugin)\ntype Widget struct{ /* no Match method */ }\n\n// after\nvar _ caddyhttp.RequestMatcherWithError = (*Widget)(nil)\n\nfunc (w *Widget) MatchWithError(r *http.Request) (bool, error) { return true, nil }","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// Compile-time proof a module can be negated inside `not`\nvar _ caddyhttp.RequestMatcherWithError = (*MyMatcher)(nil)","tryCatchPattern":null,"preventionTips":["Register matcher modules under http.matchers.* only.","Add interface guards in plugins to catch API drift at build time.","Rebuild plugins when upgrading Caddy major versions."],"tags":["caddy","plugin","matcher","type-assertion","module-system"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}