{"record":{"id":"c551549e76bc35c1","repo":"caddyserver/caddy","slug":"handshake-context-v","errorCode":null,"errorMessage":"handshake context: %v","messagePattern":"handshake context: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/connpolicy.go","lineNumber":313,"sourceCode":"\t\t\tcfg := *tlsApp.getConfigForName(hello.ServerName)\n\t\t\tif p.CertSelection != nil {\n\t\t\t\t// you would think we could just set this whether or not\n\t\t\t\t// p.CertSelection is nil, but that leads to panics if\n\t\t\t\t// it is, because cfg.CertSelection is an interface,\n\t\t\t\t// so it will have a non-nil value even if the actual\n\t\t\t\t// value underlying it is nil (sigh)\n\t\t\t\tcfg.CertSelection = p.CertSelection\n\t\t\t}\n\t\t\tcfg.DefaultServerName = p.DefaultSNI\n\t\t\tcfg.FallbackServerName = p.FallbackSNI\n\n\t\t\t// TODO: experimental: if a handshake context module is configured, allow it\n\t\t\t// to modify the context before passing it into CertMagic's GetCertificate\n\t\t\tctx := hello.Context()\n\t\t\tif p.handshakeContext != nil {\n\t\t\t\tctx, err = p.handshakeContext.HandshakeContext(hello)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"handshake context: %v\", err)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn cfg.GetCertificateWithContext(ctx, hello)\n\t\t},\n\t\tMinVersion: tls.VersionTLS12,\n\t\tMaxVersion: tls.VersionTLS13,\n\t}\n\n\t// session tickets support\n\tif tlsApp.SessionTickets != nil {\n\t\tcfg.SessionTicketsDisabled = tlsApp.SessionTickets.Disabled\n\n\t\t// session ticket key rotation\n\t\ttlsApp.SessionTickets.register(cfg)\n\t\tctx.OnCancel(func() {\n\t\t\t// do cleanup when the context is canceled because,\n\t\t\t// though unlikely, it is possible that a context","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/connpolicy.go#L295-L331","documentation":"Returned inside the per-handshake GetCertificate callback when a configured HandshakeContext module's HandshakeContext(hello) call returned an error. The module is explicitly allowed to abort the handshake by erroring; this wrapper surfaces that decision with the 'handshake context:' prefix.","triggerScenarios":"A connection policy with a handshake_context_raw module whose implementation returns an error for the current ClientHello (e.g. policy-based blocking, missing request metadata it needs, internal failure). Fails one handshake at a time, not the whole config load.","commonSituations":"Plugin-provided handshake context performing extra checks (client fingerprint filtering, mTLS metadata extraction) rejecting a client; plugin bug or missing dependency at handshake time; experimental features failing only for certain clients.","solutions":["Identify the handshake context module from the policy config and check its docs/logs for why it errors","If the error is an intentional rejection, fix the client or the module's criteria","If it is a plugin bug, gather the ClientHello details from logs and report upstream","Temporarily remove handshake_context_raw from the policy to confirm it is the source"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// In a custom HandshakeContext implementation, classify errors:\ncctx, err := hsModule.HandshakeContext(hello)\nif err != nil {\n\tif errors.Is(err, errRejectedByPolicy) {\n\t\t// intended rejection: log at info and abort handshake\n\t} else {\n\t\t// unexpected: log at error with ClientHello details for debugging\n\t}\n}","preventionTips":["Make handshake context modules distinguish policy rejections from internal failures in their error types","Load-test plugins that run per-handshake; a buggy one fails handshakes selectively and confuses debugging","Keep a way to disable the handshake_context module without full config rewrite (flag or template)"],"tags":["caddy","tls","handshake","plugin","experimental"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}