golang/go · error
tls: failed to decrypt second client hello encrypted client
Error message
tls: failed to decrypt second client hello encrypted client hello extension payload
What it means
After the HPKE context is established from ClientHello1, decrypting the ECH payload in ClientHello2 fails (decryptECHPayload error). The ciphertext is wrong, the HPKE context diverged, or the bytes were tampered with. The server sends decrypt_error.
Source
Thrown at src/crypto/tls/handshake_server_tls13.go:619
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")
}
encodedInner, err := decryptECHPayload(hs.echContext.hpkeContext, clientHello.original, payload)
if err != nil {
c.sendAlert(alertDecryptError)
return nil, errors.New("tls: failed to decrypt second client hello encrypted client hello extension payload")
}
echInner, err := decodeInnerClientHello(clientHello, encodedInner)
if err != nil {
c.sendAlert(alertIllegalParameter)
return nil, errors.New("tls: client sent invalid encrypted client hello extension")
}
clientHello = echInner
}
}
if len(clientHello.keyShares) != 1 {
c.sendAlert(alertIllegalParameter)
return nil, errors.New("tls: client didn't send one key share in second ClientHello")
}
ks := &clientHello.keyShares[0]
View on GitHub (pinned to b6b368adc5)
Solutions
- Ensure the ECH HPKE context is consistent between ClientHello1 and ClientHello2
- Verify client and server use the same HPKE KEM/KDF/AEAD algorithms
- Update the ECH library on both sides to the same draft revision
Defensive patterns
Strategy: try-catch
Try / catch
if err := tlsConn.Handshake(); err != nil {
if strings.Contains(err.Error(), "failed to decrypt second client hello encrypted client hello") {
log.Printf("ECH payload decrypt failed on retry from %v", remote)
}
c.Close()
return
} Prevention
- Keep the HPKE context consistent between ClientHello1 and ClientHello2
- Ensure client and server use the same HPKE KEM/KDF/AEAD algorithms and ECH draft revision
When it happens
Trigger: ClientHello2's ECH payload cannot be decrypted with the HPKE context from ClientHello1. Tampering, a client bug, or an HPKE algorithm/context mismatch.
Common situations: ECH client bug, network corruption, attacker tampering, HPKE KEM/KDF/AEAD mismatch between client and server.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: second client hello missing encrypted client hello exte
- 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: server sent encrypted client hello retry configs after
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/4ad6532cd0c81040.
Report an issue: GitHub.