golang/go · error
tls: handshake buffer not empty before HelloRetryRequest
Error message
tls: handshake buffer not empty before HelloRetryRequest
What it means
Before sending a HelloRetryRequest, the server checks that the client did not piggyback extra handshake messages after the first ClientHello (c.hand.Len() must be 0). If the buffer is non-empty, the client violated protocol ordering and the server sends unexpected_message. This guards against a smuggled second ClientHello.
Source
Thrown at src/crypto/tls/handshake_server_tls13.go:533
return nil
}
if hs.sentDummyCCS {
return nil
}
hs.sentDummyCCS = true
return hs.c.writeChangeCipherRecord()
}
func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) (*keyShare, error) {
c := hs.c
// Make sure the client didn't send extra handshake messages alongside
// their initial client_hello. If they sent two client_hello messages,
// we will consume the second before they respond to the server_hello.
if c.hand.Len() != 0 {
c.sendAlert(alertUnexpectedMessage)
return nil, errors.New("tls: handshake buffer not empty before HelloRetryRequest")
}
// The first ClientHello gets double-hashed into the transcript upon a
// HelloRetryRequest. See RFC 8446, Section 4.4.1.
if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil {
return nil, err
}
chHash := hs.transcript.Sum(nil)
hs.transcript.Reset()
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
hs.transcript.Write(chHash)
helloRetryRequest := &serverHelloMsg{
vers: hs.hello.vers,
random: helloRetryRequestRandom,
sessionId: hs.hello.sessionId,
cipherSuite: hs.hello.cipherSuite,
compressionMethod: hs.hello.compressionMethod,View on GitHub (pinned to b6b368adc5)
Solutions
- Client must send exactly one ClientHello and wait for ServerHello/HelloRetryRequest before sending further handshake messages
- Use a compliant TLS client library that respects handshake ordering
Defensive patterns
Strategy: try-catch
Try / catch
if err := tlsConn.Handshake(); err != nil {
if strings.Contains(err.Error(), "handshake buffer not empty before HelloRetryRequest") {
log.Printf("client sent extra messages before HRR from %v", remote)
}
c.Close()
return
} Prevention
- Use a compliant TLS client that sends one ClientHello and waits for ServerHello/HRR
- Never pipeline handshake messages ahead of the server's response
When it happens
Trigger: Client sends a ClientHello followed immediately by another handshake message before waiting for the ServerHello/HelloRetryRequest.
Common situations: Buggy or non-compliant TLS clients, fuzzers, attackers probing handshake-state handling.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: client used the legacy version field to negotiate TLS 1
- tls: TLS 1.3 client supports illegal compression methods
- tls: initial handshake had non-empty renegotiation extension
- tls: invalid or missing PSK binders
- tls: second client hello missing encrypted client hello exte
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/6a3a54945b786e66.
Report an issue: GitHub.