{"record":{"id":"07075410dae19cd8","repo":"caddyserver/caddy","slug":"loading-handshake-matchers-v","errorCode":null,"errorMessage":"loading handshake matchers: %v","messagePattern":"loading handshake matchers: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/connpolicy.go","lineNumber":57,"sourceCode":"\nfunc init() {\n\tcaddy.RegisterModule(LeafCertClientAuth{})\n}\n\n// ConnectionPolicies govern the establishment of TLS connections. It is\n// an ordered group of connection policies; the first matching policy will\n// be used to configure TLS connections at handshake-time.\ntype ConnectionPolicies []*ConnectionPolicy\n\n// Provision sets up each connection policy. It should be called\n// during the Validate() phase, after the TLS app (if any) is\n// already set up.\nfunc (cp ConnectionPolicies) Provision(ctx caddy.Context) error {\n\tfor i, pol := range cp {\n\t\t// matchers\n\t\tmods, err := ctx.LoadModule(pol, \"MatchersRaw\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"loading handshake matchers: %v\", err)\n\t\t}\n\t\tfor _, modIface := range mods.(map[string]any) {\n\t\t\tcp[i].matchers = append(cp[i].matchers, modIface.(ConnectionMatcher))\n\t\t}\n\n\t\t// enable HTTP/2 by default\n\t\tif pol.ALPN == nil {\n\t\t\tpol.ALPN = append(pol.ALPN, defaultALPN...)\n\t\t}\n\n\t\t// pre-build standard TLS config so we don't have to at handshake-time\n\t\terr = pol.buildStandardTLSConfig(ctx)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"connection policy %d: building standard TLS config: %s\", i, err)\n\t\t}\n\n\t\tif pol.ClientAuthentication != nil && len(pol.ClientAuthentication.VerifiersRaw) > 0 {\n\t\t\tclientCertValidations, err := ctx.LoadModule(pol.ClientAuthentication, \"VerifiersRaw\")","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/connpolicy.go#L39-L75","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"# before (binary built without the plugin)\n{\n\t\"match\": {\n\t\t\"sni\": [\"example.com\"],\n\t\t\"my_custom_matcher\": {...}\n\t}\n}\n\n# after (custom matcher removed or plugin built in via xcaddy)\n{\n\t\"match\": {\n\t\t\"sni\": [\"example.com\"]\n\t}\n}","handlingStrategy":"try-catch","validationCode":"# Pre-flight check that every matcher module referenced exists:\ncaddy list-modules | grep tls.handshake_match","typeGuard":null,"tryCatchPattern":"// When provisioning programmatically:\nif err := connPolicies.Provision(ctx); err != nil {\n\tif strings.HasPrefix(err.Error(), \"loading handshake matchers:\") {\n\t\t// a matcher module failed to load: inspect config's matchers map\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["caddy","tls","connection-policy","modules","configuration"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}