golang/go · error
tls: server sent an incorrect legacy version
Error message
tls: server sent an incorrect legacy version
What it means
Thrown in checkServerHelloOrHRR() when the ServerHello's legacy_version field is not 0x0303 (TLS 1.2). RFC 8446 section 4.1.3 mandates that TLS 1.3 ServerHello messages use 0x0303 in the legacy_version field for backward compatibility with middleboxes.
Source
Thrown at src/crypto/tls/handshake_client_tls13.go:180
// 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")
}
if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server did not echo the legacy session ID")
}
View on GitHub (pinned to b6b368adc5)
Solutions
- Server must set legacy_version to 0x0303 in TLS 1.3 ServerHello per RFC 8446 section 4.1.3.
- Report as a server-side compliance bug.
- If the server cannot be fixed, restrict the client to TLS 1.2.
- Test with: openssl s_client -connect host:443 -tls1_3 to see if OpenSSL also rejects it.
Defensive patterns
Strategy: try-catch
Try / catch
conn, err := tls.Dial("tcp", addr, config)
if err != nil {
if strings.Contains(err.Error(), "incorrect legacy version") {
// Server doesn't follow RFC 8446 — fall back to TLS 1.2
config.MaxVersion = tls.VersionTLS12
conn, err = tls.Dial("tcp", addr, config)
}
} Prevention
- Verify server compliance with RFC 8446 using TLS compliance scanners.
- Report incorrect legacy_version usage to server vendors.
- Keep a TLS 1.2 fallback path for legacy servers.
When it happens
Trigger: Triggered when hs.serverHello.vers != VersionTLS12 (0x0303). The client sends alertIllegalParameter. The server used an incorrect value such as 0x0301 (TLS 1.0), 0x0302 (TLS 1.1), or 0x0304 (TLS 1.3) in the legacy field.
Common situations: Non-compliant server using the actual negotiated version (0x0304) in the legacy field instead of 0x0303. Server implementation that doesn't follow RFC 8446 backward-compatibility requirements. Old server software updated partially for TLS 1.3.
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 selected an invalid version after a HelloRetryRe
- 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/611506d5dc882964.
Report an issue: GitHub.