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

  1. Server must set legacy_version to 0x0303 in TLS 1.3 ServerHello per RFC 8446 section 4.1.3.
  2. Report as a server-side compliance bug.
  3. If the server cannot be fixed, restrict the client to TLS 1.2.
  4. 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

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

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/611506d5dc882964. Report an issue: GitHub.