golang/go · error
tls: server selected TLS 1.3 using the legacy version field
Error message
tls: server selected TLS 1.3 using the legacy version field
What it means
Thrown in checkServerHelloOrHRR() when the ServerHello's supported_versions extension value is zero (absent). TLS 1.3 requires version negotiation via the supported_versions extension (RFC 8446 section 4.2.1) — using only the legacy record version field is not permitted.
Source
Thrown at src/crypto/tls/handshake_client_tls13.go:170
if hs.echContext != nil && hs.echContext.echRejected {
c.sendAlert(alertECHRequired)
return &ECHRejectionError{hs.echContext.retryConfigs}
}
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 ||View on GitHub (pinned to b6b368adc5)
Solutions
- Server must include a supported_versions extension with 0x0304 (TLS 1.3) in the ServerHello.
- If the server cannot be fixed, restrict the client to TLS 1.2: set MaxVersion: tls.VersionTLS12.
- Check for middlebox interference — test with a direct connection.
- Report as a server compliance bug to the server software vendor.
Example fix
// before — client offers TLS 1.3, server doesn't implement supported_versions
config := &tls.Config{
MinVersion: tls.VersionTLS13,
}
// after — fall back to TLS 1.2 if server is non-compliant
config := &tls.Config{
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS12,
} Defensive patterns
Strategy: try-catch
Try / catch
// Version negotiation errors are untyped strings
conn, err := tls.Dial("tcp", addr, config)
if err != nil {
if strings.Contains(err.Error(), "using the legacy version field") {
// Server doesn't implement supported_versions — fall back to TLS 1.2
config.MaxVersion = tls.VersionTLS12
conn, err = tls.Dial("tcp", addr, config)
}
} Prevention
- Test server TLS 1.3 compliance with openssl s_client -tls1_3 before deploying.
- Have a fallback TLS 1.2 configuration ready for non-compliant servers.
- Monitor for middlebox interference stripping TLS extensions.
When it happens
Trigger: Triggered when hs.serverHello.supportedVersion == 0. This means the server attempted to negotiate TLS 1.3 using only the legacy version field (0x0303) without including the mandatory supported_versions extension in the ServerHello.
Common situations: Non-compliant server that does not implement the supported_versions extension. TLS middlebox or proxy stripping extensions from the ServerHello. Server implementation that predates TLS 1.3 and incorrectly sets the version field. Broken TLS terminator in a load balancer.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- 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.
- 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/e1fe8b2716c903b1.
Report an issue: GitHub.