golang/go · error
tls: server selected TLS 1.3 in a renegotiation
Error message
tls: server selected TLS 1.3 in a renegotiation
What it means
Thrown in clientHandshakeStateTLS13.handshake() when the server selects TLS 1.3 during a renegotiation (c.handshakes > 0). RFC 8446 sections 4.1.2 and 4.1.3 explicitly prohibit renegotiation in TLS 1.3 — TLS 1.3 replaces it with post-handshake authentication and KeyUpdate.
Source
Thrown at src/crypto/tls/handshake_client_tls13.go:53
sentDummyCCS bool
suite *cipherSuiteTLS13
transcript hash.Hash
masterSecret *tls13.MasterSecret
trafficSecret []byte // client_application_traffic_secret_0
echContext *echClientContext
}
// handshake requires hs.c, hs.hello, hs.serverHello, hs.keyShareKeys, and,
// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
func (hs *clientHandshakeStateTLS13) handshake() error {
c := hs.c
// The server must not select TLS 1.3 in a renegotiation. See RFC 8446,
// sections 4.1.2 and 4.1.3.
if c.handshakes > 0 {
c.sendAlert(alertProtocolVersion)
return errors.New("tls: server selected TLS 1.3 in a renegotiation")
}
// Consistency check on the presence of a keyShare and its parameters.
if hs.keyShareKeys == nil || (hs.keyShareKeys.ecdhe == nil && hs.keyShareKeys.mlkem == nil) ||
len(hs.hello.keyShares) == 0 {
return c.sendAlert(alertInternalError)
}
if err := hs.checkServerHelloOrHRR(); err != nil {
return err
}
hs.transcript = hs.suite.hash.New()
if err := transcriptMsg(hs.hello, hs.transcript); err != nil {
return err
}
View on GitHub (pinned to b6b368adc5)
Solutions
- Do not renegotiate TLS 1.3 connections — use post-handshake authentication (tls.Conn.VerifyPeerCertificate callback) or KeyUpdate instead.
- Replace renegotiation-based connection reuse with a connection pool that creates fresh TLS connections.
- Pin MaxVersion to tls.VersionTLS12 if renegotiation is a hard requirement and cannot be refactored away.
- Refactor the re-authentication pattern: close and re-dial instead of renegotiating.
Example fix
// before — renegotiation fails on TLS 1.3
conn := // existing TLS connection
err := conn.Renegotiate(tls.RenegotiateOnceAsClient)
// after — establish a fresh connection instead
conn.Close()
conn, err = tls.Dial("tcp", addr, config) Defensive patterns
Strategy: try-catch
Try / catch
// Renegotiation errors are untyped strings
err := conn.Renegotiate(tls.RenegotiateOnceAsClient)
if err != nil {
if strings.Contains(err.Error(), "server selected TLS 1.3 in a renegotiation") {
// TLS 1.3 does not support renegotiation — dial a fresh connection
conn.Close()
conn, err = tls.Dial("tcp", addr, config)
}
} Prevention
- Never call Renegotiate() on connections that may negotiate TLS 1.3.
- Use connection pools with fresh handshakes instead of renegotiation.
- Pin MaxVersion to TLS 1.2 only if renegotiation is a hard requirement.
- Refactor re-authentication to use new connections.
When it happens
Trigger: Triggered when c.handshakes > 0 and the server's selected version is TLS 1.3. The client sends alertProtocolVersion and aborts.
Common situations: Calling Conn.Renegotiate() on a connection that the server then negotiates at TLS 1.3. Custom connection pooling that attempts renegotiation for re-authentication. Server-initiated renegotiation on an already-established TLS 1.3 connection. Code written for TLS 1.2 renegotiation that breaks under TLS 1.3.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: unexpected encrypted client hello extension in server h
- tls: server selected TLS 1.3 using the legacy version field
- tls: server selected an invalid version after a HelloRetryRe
- tls: server sent an incorrect legacy version
- tls: server sent a ServerHello extension forbidden in TLS 1.
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/3c0585d35afe28f2.
Report an issue: GitHub.