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

  1. This indicates a server-side protocol violation — verify the server's TLS implementation is correct.
  2. If the server only supports TLS 1.2, configure the client to not offer TLS 1.3 rather than forcing a mismatch.
  3. Check for middlebox or proxy interference modifying the supported_versions extension.
  4. 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

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

Related errors


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