caddyserver/caddy · error

matcher module '%s' is not a connection matcher

Error message

matcher module '%s' is not a connection matcher

What it means

When parsing a Caddyfile connection policy's matcher subdirectives (tls { match <name> ... }), each named module is unmarshaled from namespace tls.handshake_match.* and must implement the ConnectionMatcher interface (a Match(*tls.ClientHelloInfo) bool method). If the module resolves but does not implement that interface, parsing fails.

Source

Thrown at modules/caddytls/connpolicy.go:1116

	matcherMap := make(map[string]ConnectionMatcher)

	tokensByMatcherName := make(map[string][]caddyfile.Token)
	for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); {
		matcherName := d.Val()
		tokensByMatcherName[matcherName] = append(tokensByMatcherName[matcherName], d.NextSegment()...)
	}

	for matcherName, tokens := range tokensByMatcherName {
		dd := caddyfile.NewDispenser(tokens)
		dd.Next() // consume wrapper name

		unm, err := caddyfile.UnmarshalModule(dd, "tls.handshake_match."+matcherName)
		if err != nil {
			return nil, err
		}
		cm, ok := unm.(ConnectionMatcher)
		if !ok {
			return nil, fmt.Errorf("matcher module '%s' is not a connection matcher", matcherName)
		}
		matcherMap[matcherName] = cm
	}

	matcherSet := make(caddy.ModuleMap)
	for name, matcher := range matcherMap {
		jsonBytes, err := json.Marshal(matcher)
		if err != nil {
			return nil, fmt.Errorf("marshaling %T matcher: %v", matcher, err)
		}
		matcherSet[name] = jsonBytes
	}

	return matcherSet, nil
}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. If using a plugin matcher, rebuild it against your Caddy version and add the guard var _ caddytls.ConnectionMatcher = (*MyMatcher)(nil)
  2. Use only supported matchers in tls match blocks (e.g. 'match remote_ip ...')
  3. Check module registration: caddy list-modules | grep tls.handshake_match
Defensive patterns

Strategy: type-guard

Validate before calling

// (shell) confirm matcher modules are present
caddy list-modules | grep '^tls\.handshake_match\.'

Type guard

var _ caddytls.ConnectionMatcher = (*MyMatcher)(nil)

func (m *MyMatcher) Match(chi *tls.ClientHelloInfo) bool { return true }

Prevention

When it happens

Trigger: A custom or misregistered module under tls.handshake_match that lacks the Match method; a plugin namespace typo causing the wrong module to be resolved.

Common situations: XCaddy plugins implementing handshake matchers against an older/newer interface; rarely hit with stock Caddy since only built-in matchers exist.

Related errors


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