golang/go · error
tls: malformed encrypted client hello extension
Error message
tls: malformed encrypted client hello extension
What it means
Thrown during HelloRetryRequest ECH processing when the encrypted_client_hello extension in the ServerHello/HRR is present but not exactly 8 bytes long. The ECH accept confirmation value in an HRR must be exactly 8 bytes.
Source
Thrown at src/crypto/tls/handshake_client_tls13.go:260
hs.transcript.Reset()
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
hs.transcript.Write(chHash)
if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil {
return err
}
var isInnerHello bool
hello := hs.hello
if hs.echContext != nil {
chHash = hs.echContext.innerTranscript.Sum(nil)
hs.echContext.innerTranscript.Reset()
hs.echContext.innerTranscript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
hs.echContext.innerTranscript.Write(chHash)
if hs.serverHello.encryptedClientHello != nil {
if len(hs.serverHello.encryptedClientHello) != 8 {
hs.c.sendAlert(alertDecodeError)
return errors.New("tls: malformed encrypted client hello extension")
}
confTranscript := cloneHash(hs.echContext.innerTranscript, hs.suite.hash)
hrrHello := make([]byte, len(hs.serverHello.original))
copy(hrrHello, hs.serverHello.original)
hrrHello = bytes.Replace(hrrHello, hs.serverHello.encryptedClientHello, make([]byte, 8), 1)
confTranscript.Write(hrrHello)
h := hs.suite.hash.New
prk, err := hkdf.Extract(h, hs.echContext.innerHello.random, nil)
if err != nil {
c.sendAlert(alertInternalError)
return err
}
acceptConfirmation := tls13.ExpandLabel(h, prk, "hrr ech accept confirmation", confTranscript.Sum(nil), 8)
if subtle.ConstantTimeCompare(acceptConfirmation, hs.serverHello.encryptedClientHello) == 1 {
hello = hs.echContext.innerHello
c.serverName = c.config.ServerName
isInnerHello = trueView on GitHub (pinned to b6b368adc5)
Solutions
- Verify the server's ECH implementation produces exactly 8-byte accept confirmation in HelloRetryRequest.
- Ensure client and server implement compatible ECH draft versions.
- If ECH is not required, remove config.EncryptedClientHelloConfigList to disable ECH entirely.
- Report the malformed extension to the server's ECH implementation maintainer.
Defensive patterns
Strategy: try-catch
Try / catch
conn, err := tls.Dial("tcp", addr, config)
if err != nil {
if strings.Contains(err.Error(), "malformed encrypted client hello extension") {
// Server ECH HRR bug — disable ECH and retry
config.EncryptedClientHelloConfigList = nil
conn, err = tls.Dial("tcp", addr, config)
}
} Prevention
- Verify ECH draft version compatibility between client and server.
- Disable ECH if the server's implementation is non-compliant.
- Test ECH HRR paths specifically, as they are less commonly exercised.
When it happens
Trigger: Triggered when hs.echContext is not nil, hs.serverHello.encryptedClientHello is not nil, and len(hs.serverHello.encryptedClientHello) != 8 during HRR processing. The client sends alertDecodeError.
Common situations: Server bug in ECH HRR handling producing wrong-length confirmation. Mismatched ECH specification draft versions between client and server. Corrupted handshake message altering the extension length.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- tls: received malformed key_share extension
- tls: unexpected encrypted client hello extension in server h
- tls: unexpected server_name extension in server hello
- tls: server changed cipher suite after a HelloRetryRequest
- tls: unexpected encrypted client hello extension in serverHe
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/5b4de15f6e17e4c4.
Report an issue: GitHub.