{"record":{"id":"4149dd65455d7dbf","repo":"caddyserver/caddy","slug":"matcher-module-s-is-not-a-request-matcher","errorCode":null,"errorMessage":"matcher module '%s' is not a request matcher","messagePattern":"matcher module '(.+?)' is not a request matcher","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"caddyconfig/httpcaddyfile/httptype.go","lineNumber":1715,"sourceCode":"\t\tunm, ok := mod.New().(caddyfile.Unmarshaler)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"matcher module '%s' is not a Caddyfile unmarshaler\", matcherName)\n\t\t}\n\t\terr = unm.UnmarshalCaddyfile(dispenser)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif rm, ok := unm.(caddyhttp.RequestMatcherWithError); ok {\n\t\t\tmatchers[definitionName][matcherName] = caddyconfig.JSON(rm, nil)\n\t\t\treturn nil\n\t\t}\n\t\t// nolint:staticcheck\n\t\tif rm, ok := unm.(caddyhttp.RequestMatcher); ok {\n\t\t\tmatchers[definitionName][matcherName] = caddyconfig.JSON(rm, nil)\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"matcher module '%s' is not a request matcher\", matcherName)\n\t}\n\n\t// if the next token is quoted, we can assume it's not a matcher name\n\t// and that it's probably an 'expression' matcher\n\tif d.NextArg() {\n\t\tif d.Token().Quoted() {\n\t\t\t// since it was missing the matcher name, we insert a token\n\t\t\t// in front of the expression token itself; we use Clone() to\n\t\t\t// make the new token to keep the same the import location as\n\t\t\t// the next token, if this is within a snippet or imported file.\n\t\t\t// see https://github.com/caddyserver/caddy/issues/6287\n\t\t\texpressionToken := d.Token().Clone()\n\t\t\texpressionToken.Text = \"expression\"\n\t\t\terr := makeMatcher(\"expression\", []caddyfile.Token{expressionToken, d.Token()})\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil","sourceCodeStart":1697,"sourceCodeEnd":1733,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/caddyconfig/httpcaddyfile/httptype.go#L1697-L1733","documentation":"The named-matcher closure requires the unmarshaled module to be usable as an HTTP request matcher: it must implement either caddyhttp.RequestMatcherWithError or the deprecated caddyhttp.RequestMatcher. If it implements neither, it cannot be placed in a matcher set and adapt fails. Like error 141 this points to a plugin implementing the wrong (e.g. response-matcher or middleware) interface.","triggerScenarios":"A module registered in 'http.matchers.' that actually implements caddyhttp.ResponseMatcher or only caddy.Module; custom matchers built against pre-2.7 APIs without MatchWithError; trying to use a response-matcher-only module as a named request matcher.","commonSituations":"Plugin upgrade/downgrade mismatches with the Caddy core version; plugin authors forgetting that named matchers must satisfy the request-matcher interface; users attempting to use handler modules in matcher position.","solutions":["If you own the module: implement 'Match(r *http.Request) (bool, error)' (RequestMatcherWithError) on the matcher type and add a compile-time guard.","If not: rebuild with a plugin version matched to your Caddy release.","Check 'caddy list-modules' to confirm which module ID resolves to the name and whether it belongs in http.matchers.","Use the JSON config to confirm the module's supported roles if the Caddyfile path keeps failing."],"exampleFix":"// after (add to the matcher type)\nfunc (m MatchThing) Match(r *http.Request) (bool, error) {\n\treturn r.URL.Path == m.Value, nil\n}\n\nvar _ caddyhttp.RequestMatcherWithError = (*MatchThing)(nil)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"var _ caddyhttp.RequestMatcherWithError = (*MatchThing)(nil)","tryCatchPattern":null,"preventionTips":["Distinguish request matchers from response matchers when authoring plugins; only the former may appear in named matchers.","Use compile-time guards in CI builds of custom plugins.","Prefer RequestMatcherWithError over the deprecated RequestMatcher."],"tags":["caddyfile","matcher","plugin","interface-mismatch"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}