caddyserver/caddy · error
matcher is defined more than once: %s
Error message
matcher is defined more than once: %s
What it means
parseMatcherDefinitions found that a named matcher with the same name is already present in the block's matcher map. Each `@name` definition must be unique within a server block; redefining one — even with identical content — is rejected.
Source
Thrown at caddyconfig/httpcaddyfile/httptype.go:1678
for _, ms := range matcherSets {
msEncoded, err := encodeMatcherSet(ms)
if err != nil {
return nil, fmt.Errorf("server block %v: %v", sblock.block.Keys, err)
}
matcherSetsEnc = append(matcherSetsEnc, msEncoded)
}
return matcherSetsEnc, nil
}
func parseMatcherDefinitions(d *caddyfile.Dispenser, matchers map[string]caddy.ModuleMap) error {
d.Next() // advance to the first token
// this is the "name" for "named matchers"
definitionName := d.Val()
if _, ok := matchers[definitionName]; ok {
return fmt.Errorf("matcher is defined more than once: %s", definitionName)
}
matchers[definitionName] = make(caddy.ModuleMap)
// given a matcher name and the tokens following it, parse
// the tokens as a matcher module and record it
makeMatcher := func(matcherName string, tokens []caddyfile.Token) error {
// create a new dispenser from the tokens
dispenser := caddyfile.NewDispenser(tokens)
// set the matcher name (without @) in the dispenser context so
// that matcher modules can access it to use it as their name
// (e.g. regexp matchers which use the name for capture groups)
dispenser.SetContext(caddyfile.MatcherNameCtxKey, definitionName[1:])
mod, err := caddy.GetModule("http.matchers." + matcherName)
if err != nil {
return fmt.Errorf("getting matcher module '%s': %v", matcherName, err)
}View on GitHub (pinned to 50e54ee279)
Solutions
- Rename one of the duplicate matchers and update its references
- Delete the redundant definition
- If a snippet defines the matcher, do not redefine it locally and import the snippet only once
- If the snippet is imported twice, pass distinct matcher usage or restructure the snippet to take a matcher as an argument via {block}
Example fix
# before
(m) {
@foo path /foo*
}
example.com {
import m
@foo path /bar* # duplicate @foo
respond @foo 200
}
# after
(m) {
@foo path /foo*
}
example.com {
import m
respond @foo 200
} Defensive patterns
Strategy: validation
Validate before calling
# Reject duplicate matcher names per block during generation
for block in blocks:
names = [m.name for m in block.matcher_defs]
if len(names) != len(set(names)):
raise ConfigError('duplicate matcher: ' + str(dups(names))) Prevention
- Prefix snippet matcher names (e.g. @snip_foo) to avoid collisions with local names
- Import matcher-defining snippets at most once per block
- Rename, never redefine, when refactoring matchers
When it happens
Trigger: Two `@foo ...` declarations in the same site block, or importing a snippet that defines `@foo` into a block that already defines `@foo` (e.g. the snippet is imported twice, or the block duplicates it manually).
Common situations: Reusable snippets containing named matchers imported multiple times or alongside local definitions; merging configs that each defined the same matcher name; refactoring that leaves an old definition next to a new one.
Related errors
- unrecognized matcher name: %+v
- server block %v: %v
- duplicate ID '%s' found at %s and %s
- consolidating TLS connection policies for server %d: %v
- applying global server options: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/aa5e7e2e4abd70eb.
Report an issue: GitHub.