caddyserver/caddy · error
loading handshake matchers: %v
Error message
loading handshake matchers: %v
What it means
Returned by ConnectionPolicies.Provision when ctx.LoadModule fails to instantiate one of the handshake matchers listed in a connection policy's MatchersRaw map (e.g. tls.handshake_match.sni, tls.handshake_match.alpn, tls.handshake_match.remote_ip). The wrapped error usually identifies the module name and the underlying cause (unknown module, bad subconfig, provisioning failure inside the matcher).
Source
Thrown at modules/caddytls/connpolicy.go:57
func init() {
caddy.RegisterModule(LeafCertClientAuth{})
}
// ConnectionPolicies govern the establishment of TLS connections. It is
// an ordered group of connection policies; the first matching policy will
// be used to configure TLS connections at handshake-time.
type ConnectionPolicies []*ConnectionPolicy
// Provision sets up each connection policy. It should be called
// during the Validate() phase, after the TLS app (if any) is
// already set up.
func (cp ConnectionPolicies) Provision(ctx caddy.Context) error {
for i, pol := range cp {
// matchers
mods, err := ctx.LoadModule(pol, "MatchersRaw")
if err != nil {
return fmt.Errorf("loading handshake matchers: %v", err)
}
for _, modIface := range mods.(map[string]any) {
cp[i].matchers = append(cp[i].matchers, modIface.(ConnectionMatcher))
}
// enable HTTP/2 by default
if pol.ALPN == nil {
pol.ALPN = append(pol.ALPN, defaultALPN...)
}
// pre-build standard TLS config so we don't have to at handshake-time
err = pol.buildStandardTLSConfig(ctx)
if err != nil {
return fmt.Errorf("connection policy %d: building standard TLS config: %s", i, err)
}
if pol.ClientAuthentication != nil && len(pol.ClientAuthentication.VerifiersRaw) > 0 {
clientCertValidations, err := ctx.LoadModule(pol.ClientAuthentication, "VerifiersRaw")View on GitHub (pinned to 50e54ee279)
Solutions
- Read the wrapped error: it names the module that failed and why
- Run 'caddy list-modules' and confirm every tls.handshake_match.* referenced in the config is present
- If a plugin matcher is missing, rebuild with xcaddy including the plugin, or remove that matcher
- Fix the matcher's arguments (e.g. empty SNI value, malformed IP range in remote_ip)
Example fix
# before (binary built without the plugin)
{
"match": {
"sni": ["example.com"],
"my_custom_matcher": {...}
}
}
# after (custom matcher removed or plugin built in via xcaddy)
{
"match": {
"sni": ["example.com"]
}
} Defensive patterns
Strategy: try-catch
Validate before calling
# Pre-flight check that every matcher module referenced exists: caddy list-modules | grep tls.handshake_match
Try / catch
// When provisioning programmatically:
if err := connPolicies.Provision(ctx); err != nil {
if strings.HasPrefix(err.Error(), "loading handshake matchers:") {
// a matcher module failed to load: inspect config's matchers map
}
return err
} Prevention
- Pin xcaddy builds with the plugins your config depends on, and list-modules after every rebuild
- Version-control configs alongside the build file (xcaddy.json/plugins list) so they drift together
- Validate configs in CI on the same binary that serves them
When it happens
Trigger: JSON config with a matchers object referencing a module ID that is not compiled into the binary (e.g. a plugin matcher without the plugin built in), or a matcher whose own Provision fails. Via Caddyfile: an SNI/ALPN matcher whose inline values are malformed so the matcher's UnmarshalCaddyfile produces an error.
Common situations: Custom build (xcaddy) missing a plugin that the config was written for; migrating a config between Caddy versions where a matcher module was renamed or its schema changed; typo in the matcher name.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- connection policy %d: building standard TLS config: %s
- loading client cert verifiers: %v
- loading handshake context module: %v
- invalid TLS renegotiation level: %v
- URL is required
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/07075410dae19cd8.
Report an issue: GitHub.