golang/go · error
tls: initial handshake had non-empty renegotiation extension
Error message
tls: initial handshake had non-empty renegotiation extension
What it means
TLS 1.3 removed renegotiation entirely; the renegotiation_info (0xff01) extension must not appear with content on an initial handshake (RFC 8446 §4.2.2). If hs.clientHello.secureRenegotiation is non-empty on the first handshake, the server sends handshake_failure and aborts.
Source
Thrown at src/crypto/tls/handshake_server_tls13.go:157
break
}
}
if len(hs.clientHello.compressionMethods) != 1 ||
hs.clientHello.compressionMethods[0] != compressionNone {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: TLS 1.3 client supports illegal compression methods")
}
hs.hello.random = make([]byte, 32)
if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil {
c.sendAlert(alertInternalError)
return err
}
if len(hs.clientHello.secureRenegotiation) != 0 {
c.sendAlert(alertHandshakeFailure)
return errors.New("tls: initial handshake had non-empty renegotiation extension")
}
if hs.clientHello.earlyData && c.quic != nil {
if len(hs.clientHello.pskIdentities) == 0 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: early_data without pre_shared_key")
}
} else if hs.clientHello.earlyData {
// See RFC 8446, Section 4.2.10 for the complicated behavior required
// here. The scenario is that a different server at our address offered
// to accept early data in the past, which we can't handle. For now, all
// 0-RTT enabled session tickets need to expire before a Go server can
// replace a server or join a pool. That's the same requirement that
// applies to mixing or replacing with any TLS 1.2 server.
c.sendAlert(alertUnsupportedExtension)
return errors.New("tls: client sent unexpected early data")
}
View on GitHub (pinned to b6b368adc5)
Solutions
- Update the client to a TLS 1.3-aware version that omits renegotiation_info for 1.3 handshakes
- If a middlebox injects the extension, remove or upgrade the middlebox
- Disable client-side renegotiation_info advertisement for TLS 1.3 connections
Defensive patterns
Strategy: try-catch
Try / catch
if err := tlsConn.Handshake(); err != nil {
if strings.Contains(err.Error(), "non-empty renegotiation extension") {
log.Printf("client sent renegotiation_info on TLS 1.3 from %v", remote)
}
c.Close()
return
} Prevention
- Update TLS 1.2-era clients so they omit renegotiation_info for TLS 1.3
- Audit middleboxes that may inject renegotiation_info
When it happens
Trigger: A client sends a non-empty renegotiation_info extension in the initial ClientHello. Common with TLS 1.2-aware clients that always populate renegotiation_info, or middleboxes that inject it.
Common situations: Older OpenSSL/GnuTLS clients that unconditionally carry renegotiation_info; legacy middleboxes/proxies; TLS 1.2 clients being upgraded mid-stream to talk to a TLS 1.3 server.
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: invalid or missing PSK binders
- tls: handshake buffer not empty before HelloRetryRequest
- tls: missing signature_algorithms from TLS 1.2 peer
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/567a47951db092ba.
Report an issue: GitHub.