golang/go · error
tls: second client hello missing encrypted client hello exte
Error message
tls: second client hello missing encrypted client hello extension
What it means
When ECH (Encrypted Client Hello) was offered in the first ClientHello, the second ClientHello (after HelloRetryRequest) must also carry the ECH extension. If hs.echContext != nil but clientHello.encryptedClientHello is empty, the server sends missing_extension.
Source
Thrown at src/crypto/tls/handshake_server_tls13.go:596
return nil, err
}
// clientHelloMsg is not included in the transcript.
msg, err := c.readHandshake(nil)
if err != nil {
return nil, err
}
clientHello, ok := msg.(*clientHelloMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return nil, unexpectedMessageError(clientHello, msg)
}
if hs.echContext != nil {
if len(clientHello.encryptedClientHello) == 0 {
c.sendAlert(alertMissingExtension)
return nil, errors.New("tls: second client hello missing encrypted client hello extension")
}
echType, echCiphersuite, configID, encap, payload, err := parseECHExt(clientHello.encryptedClientHello)
if err != nil {
c.sendAlert(alertDecodeError)
return nil, errors.New("tls: client sent invalid encrypted client hello extension")
}
if echType == outerECHExt && hs.echContext.inner || echType == innerECHExt && !hs.echContext.inner {
c.sendAlert(alertDecodeError)
return nil, errors.New("tls: unexpected switch in encrypted client hello extension type")
}
if echType == outerECHExt {
if echCiphersuite != hs.echContext.ciphersuite || configID != hs.echContext.configID || len(encap) != 0 {
c.sendAlert(alertIllegalParameter)
return nil, errors.New("tls: second client hello encrypted client hello extension does not match")
}View on GitHub (pinned to b6b368adc5)
Solutions
- Ensure ClientHello2 includes the ECH extension consistent with the HRR's ECH configuration
- Update the ECH-capable client library to a version that retries ECH correctly
Defensive patterns
Strategy: try-catch
Try / catch
if err := tlsConn.Handshake(); err != nil {
if strings.Contains(err.Error(), "second client hello missing encrypted client hello") {
log.Printf("ECH dropped on retry from %v", remote)
}
c.Close()
return
} Prevention
- ECH clients must resend the ECH extension in ClientHello2 after HelloRetryRequest
- Use an ECH library that correctly handles the HRR retry flow
When it happens
Trigger: Client offers ECH in ClientHello1, receives HRR, but omits the ECH extension in ClientHello2.
Common situations: Buggy ECH client that drops the extension on retry; partial ECH support; ECH draft-revision drift between client and server.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: client sent invalid encrypted client hello extension
- tls: unexpected switch in encrypted client hello extension t
- tls: second client hello encrypted client hello extension do
- tls: failed to decrypt second client hello encrypted client
- tls: malformed encrypted client hello extension
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/c641d0560bb2435f.
Report an issue: GitHub.