golang/go · error
tls: initial handshake had non-empty renegotiation extension
Error message
tls: initial handshake had non-empty renegotiation extension
What it means
On an initial (non-renegotiation) handshake, the client sent a non-empty secure_renegotiation extension data field. RFC 5746 requires that field to be empty on the first handshake; only renegotiation handshakes carry the previous verify_data. Sending renegotiation data on the initial handshake is a handshake_failure.
Source
Thrown at src/crypto/tls/handshake_server.go:255
// Downgrade protection canaries. See RFC 8446, Section 4.1.3.
maxVers := c.config.maxSupportedVersion(roleServer, c.quic != nil)
if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary {
if c.vers == VersionTLS12 {
copy(serverRandom[24:], downgradeCanaryTLS12)
} else {
copy(serverRandom[24:], downgradeCanaryTLS11)
}
serverRandom = serverRandom[:24]
}
_, err := io.ReadFull(c.config.rand(), serverRandom)
if 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")
}
hs.hello.extendedMasterSecret = hs.clientHello.extendedMasterSecret
hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported
hs.hello.compressionMethod = compressionNone
if len(hs.clientHello.serverName) > 0 {
c.serverName = hs.clientHello.serverName
}
selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols, false)
if err != nil {
c.sendAlert(alertNoApplicationProtocol)
return err
}
hs.hello.alpnProtocol = selectedProto
c.clientProtocol = selectedProto
hs.cert, err = c.config.getCertificate(clientHelloInfo(hs.ctx, c, hs.clientHello))View on GitHub (pinned to b6b368adc5)
Solutions
- Use a compliant client library — RFC 5746 mandates an empty renegotiation_info on the initial handshake.
- Restart the client's TLS state (clear cached sessions/renegotiation context) and reconnect.
- If the client is your own, audit how renegotiation_info is populated before the first handshake completes.
- Treat persistent occurrences as a sign of a tampered or malicious client.
Defensive patterns
Strategy: validation
Validate before calling
// Client: ensure renegotiation_info is empty on the initial handshake. // Standard libraries do this; if you maintain a custom stack, never seed // renegotiation data before the first handshake completes.
Try / catch
// Server: log as a protocol anomaly.
if err != nil && strings.Contains(err.Error(), "non-empty renegotiation extension") {
log.Warn("possible CVE-2009-3555 probe", "remote", conn.RemoteAddr())
} Prevention
- Reset renegotiation state on every new connection.
- Update legacy TLS stacks to RFC 5746-compliant versions.
- Monitor for scanning activity that triggers this alert.
When it happens
Trigger: processClientHello checks len(hs.clientHello.secureRenegotiation) != 0 on the initial handshake and alerts. The peer transmitted a renegotiation_info extension with non-empty contents on its first ClientHello.
Common situations: A buggy client reusing renegotiation state across connections, a TLS stack that incorrectly seeds the renegotiation field, or a malicious peer probing for CVE-2009-3555 style renegotiation vulnerabilities.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: client does not support uncompressed connections
- tls: client certificate used with invalid signature algorith
- tls: server selected TLS 1.3 in a renegotiation
- tls: received new session ticket from a client
- tls: FIPS 140-3 requires the use of Extended Master Secret
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/c41667a273f102b9.
Report an issue: GitHub.