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
- Define the named matcher at the top of the same site block: `@foo path /foo*`
- Fix the typo so the reference matches the definition exactly (including the @)
- If the matcher lives in a snippet, import the snippet that defines it into the same block
- 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
- Define named matchers at the top of the same site block
- Keep one canonical spelling; grep @name usages after renames
- Remember matchers are block-scoped — snippets must define what they reference
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
- server block %v: %v
- matcher is defined more than once: %s
- consolidating TLS connection policies for server %d: %v
- applying global server options: %v
- server listening on %v is configured for HTTPS and cannot na
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/00ef52d0bec4bff6.
Report an issue: GitHub.