golang/go · error

tls: initial handshake had non-empty renegotiation extension

Error message

tls: initial handshake had non-empty renegotiation extension

What it means

Per RFC 5746, on the very first handshake (c.handshakes == 0) when the server signals secure_renegotiation support, the renegotiation_info extension contents must be empty. If hs.serverHello.secureRenegotiation is non-empty on the initial handshake, the server is violating the protocol and the handshake fails with alertHandshakeFailure.

Source

Thrown at src/crypto/tls/handshake_client.go:910

	supportsPointFormat := false
	offeredNonCompressedFormat := false
	for _, format := range hs.serverHello.supportedPoints {
		if format == pointFormatUncompressed {
			supportsPointFormat = true
		} else {
			offeredNonCompressedFormat = true
		}
	}
	if !supportsPointFormat && offeredNonCompressedFormat {
		return false, errors.New("tls: server offered only incompatible point formats")
	}

	if c.handshakes == 0 && hs.serverHello.secureRenegotiationSupported {
		c.secureRenegotiation = true
		if len(hs.serverHello.secureRenegotiation) != 0 {
			c.sendAlert(alertHandshakeFailure)
			return false, errors.New("tls: initial handshake had non-empty renegotiation extension")
		}
	}

	if c.handshakes > 0 && c.secureRenegotiation {
		var expectedSecureRenegotiation [24]byte
		copy(expectedSecureRenegotiation[:], c.clientFinished[:])
		copy(expectedSecureRenegotiation[12:], c.serverFinished[:])
		if !bytes.Equal(hs.serverHello.secureRenegotiation, expectedSecureRenegotiation[:]) {
			c.sendAlert(alertHandshakeFailure)
			return false, errors.New("tls: incorrect renegotiation extension contents")
		}
	}

	if err := checkALPN(hs.hello.alpnProtocols, hs.serverHello.alpnProtocol, false); err != nil {
		c.sendAlert(alertUnsupportedExtension)
		return false, err
	}
	c.clientProtocol = hs.serverHello.alpnProtocol

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Patch the server to comply with RFC 5746 (empty renegotiation_info on initial handshake).
  2. Capture the ServerHello and report the defect to the server operator.
  3. Investigate the path for tampering.
Defensive patterns

Strategy: try-catch

Type guard

func isInitialHandshakeNonEmptyReneg(err error) bool {
    return err != nil && strings.Contains(err.Error(), "initial handshake had non-empty renegotiation extension")
}

Try / catch

if _, err := tls.Dial("tcp", addr, cfg); err != nil {
    if isInitialHandshakeNonEmptyReneg(err) {
        reportServerDefect(addr, err) // RFC 5746 violation
    }
}

Prevention

When it happens

Trigger: Server sending non-empty renegotiation_info on the initial handshake; middlebox injecting data into the extension; corrupted ServerHello.

Common situations: Custom or outdated TLS stacks; rare in practice.

Understand the failure class

Related errors


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