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

  1. Rename one of the duplicate matchers and update its references
  2. Delete the redundant definition
  3. If a snippet defines the matcher, do not redefine it locally and import the snippet only once
  4. 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

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


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