caddyserver/caddy · error

unrecognized matcher name: %+v

Error message

unrecognized matcher name: %+v

What it means

A token starting with `@` (the named-matcher prefix) was referenced in a directive's matcher position, but no matcher with that name was defined in the current server block. Named matchers must be declared with `@name ...` inside the same site block before use.

Source

Thrown at caddyconfig/httpcaddyfile/httptype.go:1587

	// matcher tokens can be wildcards, simple path matchers,
	// or refer to a pre-defined matcher by some name
	if tkn.Text == "*" {
		// match all requests == no matchers, so nothing to do
		return nil, true, nil
	}

	// convenient way to specify a single path match
	if strings.HasPrefix(tkn.Text, "/") {
		return caddy.ModuleMap{
			"path": caddyconfig.JSON(caddyhttp.MatchPath{tkn.Text}, warnings),
		}, true, nil
	}

	// pre-defined matcher
	if strings.HasPrefix(tkn.Text, matcherPrefix) {
		m, ok := matcherDefs[tkn.Text]
		if !ok {
			return nil, false, fmt.Errorf("unrecognized matcher name: %+v", tkn.Text)
		}
		return m, true, nil
	}

	return nil, false, nil
}

func (st *ServerType) compileEncodedMatcherSets(sblock serverBlock) ([]caddy.ModuleMap, error) {
	type hostPathPair struct {
		hostm caddyhttp.MatchHost
		pathm caddyhttp.MatchPath
	}

	// keep routes with common host and path matchers together
	var matcherPairs []*hostPathPair

	var catchAllHosts bool
	for _, addr := range sblock.parsedKeys {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Define the named matcher at the top of the same site block: `@foo path /foo*`
  2. Fix the typo so the reference matches the definition exactly (including the @)
  3. If the matcher lives in a snippet, import the snippet that defines it into the same block
  4. Move the shared matcher definition into each site block that uses it

Example fix

# before
example.com {
  @api path /api/*
  reverse_proxy @backend localhost:9000  # @backend not defined
}
# after
example.com {
  @api path /api/*
  reverse_proxy @api localhost:9000
}
Defensive patterns

Strategy: validation

Validate before calling

# Collect matcher definitions per block before adapting
for block in blocks:
    defined = {m.name for m in block.matcher_defs}
    for ref in block.matcher_refs:
        assert ref in defined, f'undefined matcher {ref} in {block.key}'

Prevention

When it happens

Trigger: Writing `respond @foo 200` (or `reverse_proxy @api ...`) where `@foo` was never declared, declaring the matcher in a different site block than where it is referenced, or a typo such as `@apistatus` vs `@apistaus`. parseSegmentAsSubroute -> matcher token parsing hits matcherDefs lookup failure.

Common situations: Renaming a matcher and missing a usage site; expecting named matchers to be global when they are per-site-block; snippets referencing matchers the importing block does not define (use `import` of the matcher definition too).

Related errors


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