golang/go · error

tls: initial handshake had non-empty renegotiation extension

Error message

tls: initial handshake had non-empty renegotiation extension

What it means

TLS 1.3 removed renegotiation entirely; the renegotiation_info (0xff01) extension must not appear with content on an initial handshake (RFC 8446 §4.2.2). If hs.clientHello.secureRenegotiation is non-empty on the first handshake, the server sends handshake_failure and aborts.

Source

Thrown at src/crypto/tls/handshake_server_tls13.go:157

			break
		}
	}

	if len(hs.clientHello.compressionMethods) != 1 ||
		hs.clientHello.compressionMethods[0] != compressionNone {
		c.sendAlert(alertIllegalParameter)
		return errors.New("tls: TLS 1.3 client supports illegal compression methods")
	}

	hs.hello.random = make([]byte, 32)
	if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil {
		c.sendAlert(alertInternalError)
		return err
	}

	if len(hs.clientHello.secureRenegotiation) != 0 {
		c.sendAlert(alertHandshakeFailure)
		return errors.New("tls: initial handshake had non-empty renegotiation extension")
	}

	if hs.clientHello.earlyData && c.quic != nil {
		if len(hs.clientHello.pskIdentities) == 0 {
			c.sendAlert(alertIllegalParameter)
			return errors.New("tls: early_data without pre_shared_key")
		}
	} else if hs.clientHello.earlyData {
		// See RFC 8446, Section 4.2.10 for the complicated behavior required
		// here. The scenario is that a different server at our address offered
		// to accept early data in the past, which we can't handle. For now, all
		// 0-RTT enabled session tickets need to expire before a Go server can
		// replace a server or join a pool. That's the same requirement that
		// applies to mixing or replacing with any TLS 1.2 server.
		c.sendAlert(alertUnsupportedExtension)
		return errors.New("tls: client sent unexpected early data")
	}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Update the client to a TLS 1.3-aware version that omits renegotiation_info for 1.3 handshakes
  2. If a middlebox injects the extension, remove or upgrade the middlebox
  3. Disable client-side renegotiation_info advertisement for TLS 1.3 connections
Defensive patterns

Strategy: try-catch

Try / catch

if err := tlsConn.Handshake(); err != nil {
    if strings.Contains(err.Error(), "non-empty renegotiation extension") {
        log.Printf("client sent renegotiation_info on TLS 1.3 from %v", remote)
    }
    c.Close()
    return
}

Prevention

When it happens

Trigger: A client sends a non-empty renegotiation_info extension in the initial ClientHello. Common with TLS 1.2-aware clients that always populate renegotiation_info, or middleboxes that inject it.

Common situations: Older OpenSSL/GnuTLS clients that unconditionally carry renegotiation_info; legacy middleboxes/proxies; TLS 1.2 clients being upgraded mid-stream to talk to a TLS 1.3 server.

Understand the failure class

Related errors


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