golang/go · error
tls: server selected an invalid version after a HelloRetryRe
Error message
tls: server selected an invalid version after a HelloRetryRequest
What it means
Thrown in checkServerHelloOrHRR() when the ServerHello's supported_versions extension contains a value other than TLS 1.3 (0x0304). Since the client entered the TLS 1.3 handshake code path, any version other than 1.3 in supported_versions is invalid.
Source
Thrown at src/crypto/tls/handshake_client_tls13.go:175
c.isHandshakeComplete.Store(true)
return nil
}
// checkServerHelloOrHRR does validity checks that apply to both ServerHello and
// HelloRetryRequest messages. It sets hs.suite.
func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
c := hs.c
if hs.serverHello.supportedVersion == 0 {
c.sendAlert(alertMissingExtension)
return errors.New("tls: server selected TLS 1.3 using the legacy version field")
}
if hs.serverHello.supportedVersion != VersionTLS13 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server selected an invalid version after a HelloRetryRequest")
}
if hs.serverHello.vers != VersionTLS12 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server sent an incorrect legacy version")
}
if hs.serverHello.ocspStapling ||
hs.serverHello.ticketSupported ||
hs.serverHello.extendedMasterSecret ||
hs.serverHello.secureRenegotiationSupported ||
len(hs.serverHello.secureRenegotiation) != 0 ||
len(hs.serverHello.alpnProtocol) != 0 ||
len(hs.serverHello.scts) != 0 {
c.sendAlert(alertUnsupportedExtension)
return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
}
View on GitHub (pinned to b6b368adc5)
Solutions
- This indicates a server-side protocol violation — verify the server's TLS implementation is correct.
- If the server only supports TLS 1.2, configure the client to not offer TLS 1.3 rather than forcing a mismatch.
- Check for middlebox or proxy interference modifying the supported_versions extension.
- Report the bug to the server software vendor with a packet capture.
Defensive patterns
Strategy: try-catch
Try / catch
conn, err := tls.Dial("tcp", addr, config)
if err != nil {
if strings.Contains(err.Error(), "invalid version after a HelloRetryRequest") {
// Server protocol violation — attempt TLS 1.2 fallback
config.MaxVersion = tls.VersionTLS12
conn, err = tls.Dial("tcp", addr, config)
}
} Prevention
- Use Wireshark to capture and verify the supported_versions extension in server responses.
- Report server-side TLS 1.3 implementation bugs to the vendor.
- Maintain a fallback configuration for non-compliant servers.
When it happens
Trigger: Triggered when hs.serverHello.supportedVersion is non-zero but != VersionTLS13. The client sends alertIllegalParameter. This can occur in either an initial ServerHello or a HelloRetryRequest.
Common situations: Server bug negotiating a wrong version in the supported_versions extension. TLS downgrade attack where an attacker modifies the extension value. Non-compliant server mixing TLS version fields. HelloRetryRequest with a version the client didn't expect.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: server selected TLS 1.3 using the legacy version field
- tls: server sent an incorrect legacy version
- tls: server sent a ServerHello extension forbidden in TLS 1.
- tls: server did not echo the legacy session ID
- tls: server sent an unnecessary HelloRetryRequest message
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/844210caa6a43163.
Report an issue: GitHub.