caddyserver/caddy · error

matcher module '%s' is not a request matcher

Error message

matcher module '%s' is not a request matcher

What it means

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.

Source

Thrown at caddyconfig/httpcaddyfile/httptype.go:1715

		unm, ok := mod.New().(caddyfile.Unmarshaler)
		if !ok {
			return fmt.Errorf("matcher module '%s' is not a Caddyfile unmarshaler", matcherName)
		}
		err = unm.UnmarshalCaddyfile(dispenser)
		if err != nil {
			return err
		}

		if rm, ok := unm.(caddyhttp.RequestMatcherWithError); ok {
			matchers[definitionName][matcherName] = caddyconfig.JSON(rm, nil)
			return nil
		}
		// nolint:staticcheck
		if rm, ok := unm.(caddyhttp.RequestMatcher); ok {
			matchers[definitionName][matcherName] = caddyconfig.JSON(rm, nil)
			return nil
		}
		return fmt.Errorf("matcher module '%s' is not a request matcher", matcherName)
	}

	// if the next token is quoted, we can assume it's not a matcher name
	// and that it's probably an 'expression' matcher
	if d.NextArg() {
		if d.Token().Quoted() {
			// since it was missing the matcher name, we insert a token
			// in front of the expression token itself; we use Clone() to
			// make the new token to keep the same the import location as
			// the next token, if this is within a snippet or imported file.
			// see https://github.com/caddyserver/caddy/issues/6287
			expressionToken := d.Token().Clone()
			expressionToken.Text = "expression"
			err := makeMatcher("expression", []caddyfile.Token{expressionToken, d.Token()})
			if err != nil {
				return err
			}
			return nil

View on GitHub (pinned to 50e54ee279)

Solutions

  1. If you own the module: implement 'Match(r *http.Request) (bool, error)' (RequestMatcherWithError) on the matcher type and add a compile-time guard.
  2. If not: rebuild with a plugin version matched to your Caddy release.
  3. Check 'caddy list-modules' to confirm which module ID resolves to the name and whether it belongs in http.matchers.
  4. Use the JSON config to confirm the module's supported roles if the Caddyfile path keeps failing.

Example fix

// after (add to the matcher type)
func (m MatchThing) Match(r *http.Request) (bool, error) {
	return r.URL.Path == m.Value, nil
}

var _ caddyhttp.RequestMatcherWithError = (*MatchThing)(nil)
Defensive patterns

Strategy: type-guard

Type guard

var _ caddyhttp.RequestMatcherWithError = (*MatchThing)(nil)

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/4149dd65455d7dbf. Report an issue: GitHub.