golang/go · error
tls: server sent a cookie in a normal ServerHello
Error message
tls: server sent a cookie in a normal ServerHello
What it means
In a genuine ServerHello (not an HRR), RFC 8446 §4.2.2 forbids the cookie extension; cookies belong only in HelloRetryRequest. Go detects a non-empty hs.serverHello.cookie and sends an `unsupported_extension` alert. Indicates a server that is misusing the cookie extension.
Source
Thrown at src/crypto/tls/handshake_client_tls13.go:422
if err := hs.checkServerHelloOrHRR(); err != nil {
return err
}
c.didHRR = true
return nil
}
func (hs *clientHandshakeStateTLS13) processServerHello() error {
c := hs.c
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
c.sendAlert(alertUnexpectedMessage)
return errors.New("tls: server sent two HelloRetryRequest messages")
}
if len(hs.serverHello.cookie) != 0 {
c.sendAlert(alertUnsupportedExtension)
return errors.New("tls: server sent a cookie in a normal ServerHello")
}
if hs.serverHello.selectedGroup != 0 {
c.sendAlert(alertDecodeError)
return errors.New("tls: malformed key_share extension")
}
if hs.serverHello.serverShare.group == 0 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server did not send a key share")
}
if !slices.ContainsFunc(hs.hello.keyShares, func(ks keyShare) bool {
return ks.group == hs.serverHello.serverShare.group
}) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server selected unsupported group")
}
View on GitHub (pinned to b6b368adc5)
Solutions
- Report to the server operator — the cookie extension is HRR-only.
- Capture the handshake with Wireshark to confirm the offending extension is coming from the server, not a proxy.
- Eliminate any TLS-terminating proxy/load-balancer in the path that may be injecting extensions.
- If self-operated, fix the server's ServerHello encoder to omit cookie outside HRR.
Defensive patterns
Strategy: try-catch
Try / catch
conn, err := tls.Dial("tcp", addr, cfg)
if err != nil && strings.Contains(err.Error(), "cookie in a normal ServerHello") {
// cookie is HRR-only; the server or a proxy is misbehaving
log.Printf("peer %s injected a cookie outside HRR", addr)
return err
} Prevention
- Audit TLS-terminating proxies for extensions they inject.
- Prefer direct connections during diagnosis to isolate the source of unexpected extensions.
- Test new server releases against the Go client before deployment.
When it happens
Trigger: ServerHello (random != helloRetryRequestRandom) carries a non-empty cookie field. Reached in processServerHello during a normal TLS 1.3 handshake.
Common situations: Server implementation that always echoes the cookie, fuzz-generated ServerHello, or a malicious peer probing the client. Normal OpenSSL/BoringSSL/Go servers never do this.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: server sent two HelloRetryRequest messages
- tls: malformed key_share extension
- tls: server did not send a key share
- tls: server selected an invalid PSK
- tls: server selected an invalid PSK and cipher suite pair
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/70713ef9686ca310.
Report an issue: GitHub.