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
- If using a plugin matcher, rebuild it against your Caddy version and add the guard var _ caddytls.ConnectionMatcher = (*MyMatcher)(nil)
- Use only supported matchers in tls match blocks (e.g. 'match remote_ip ...')
- 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
- Add interface guards in custom matcher plugins
- Prefer standard matchers (remote_ip, sni) available in stock builds
- Rebuild xcaddy plugins on every core upgrade
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
- leaf module '%s' is not a leaf certificate loader
- 'ca' module '%s' is not a certificate pool provider
- marshaling %T matcher: %v
- consolidating TLS connection policies for server %d: %v
- server listening on %v is HTTP, but attempts to configure TL
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/d6cd9bb83f1aabae.
Report an issue: GitHub.