caddyserver/caddy · error
handshake context: %v
Error message
handshake context: %v
What it means
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.
Source
Thrown at modules/caddytls/connpolicy.go:313
cfg := *tlsApp.getConfigForName(hello.ServerName)
if p.CertSelection != nil {
// you would think we could just set this whether or not
// p.CertSelection is nil, but that leads to panics if
// it is, because cfg.CertSelection is an interface,
// so it will have a non-nil value even if the actual
// value underlying it is nil (sigh)
cfg.CertSelection = p.CertSelection
}
cfg.DefaultServerName = p.DefaultSNI
cfg.FallbackServerName = p.FallbackSNI
// TODO: experimental: if a handshake context module is configured, allow it
// to modify the context before passing it into CertMagic's GetCertificate
ctx := hello.Context()
if p.handshakeContext != nil {
ctx, err = p.handshakeContext.HandshakeContext(hello)
if err != nil {
return nil, fmt.Errorf("handshake context: %v", err)
}
}
return cfg.GetCertificateWithContext(ctx, hello)
},
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS13,
}
// session tickets support
if tlsApp.SessionTickets != nil {
cfg.SessionTicketsDisabled = tlsApp.SessionTickets.Disabled
// session ticket key rotation
tlsApp.SessionTickets.register(cfg)
ctx.OnCancel(func() {
// do cleanup when the context is canceled because,
// though unlikely, it is possible that a contextView on GitHub (pinned to 50e54ee279)
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
Defensive patterns
Strategy: try-catch
Try / catch
// In a custom HandshakeContext implementation, classify errors:
cctx, err := hsModule.HandshakeContext(hello)
if err != nil {
if errors.Is(err, errRejectedByPolicy) {
// intended rejection: log at info and abort handshake
} else {
// unexpected: log at error with ClientHello details for debugging
}
} Prevention
- 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)
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- source %T returned a nil certificate
- loading handshake context module: %v
- dropping connection
- no server TLS configuration available for ClientHello: %+v
- trust_pool module '%s' is not a certificate pool provider
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/c551549e76bc35c1.
Report an issue: GitHub.