golang/go · error

tls: server resumed a session with a different version

Error message

tls: server resumed a session with a different version

What it means

When processServerHello detects resumption (hs.serverResumedSession() returned true), the resumed session's recorded version must equal c.vers from this handshake. If hs.session.version != c.vers the server resumed a session across an incompatible version, which TLS forbids and aborts with alertHandshakeFailure.

Source

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

			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

	c.scts = hs.serverHello.scts

	if !hs.serverResumedSession() {
		return false, nil
	}

	if hs.session.version != c.vers {
		c.sendAlert(alertHandshakeFailure)
		return false, errors.New("tls: server resumed a session with a different version")
	}

	if hs.session.cipherSuite != hs.suite.id {
		c.sendAlert(alertHandshakeFailure)
		return false, errors.New("tls: server resumed a session with a different cipher suite")
	}

	// RFC 7627, Section 5.3
	if hs.session.extMasterSecret != hs.serverHello.extendedMasterSecret {
		c.sendAlert(alertHandshakeFailure)
		return false, errors.New("tls: server resumed a session with a different EMS extension")
	}

	// Restore master secret and certificates from previous state
	hs.masterSecret = hs.session.secret
	c.extMasterSecret = hs.session.extMasterSecret
	c.peerCertificates = hs.session.peerCertificates
	c.verifiedChains = hs.session.verifiedChains

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Clear/rotate the server's session ticket key so stale tickets are not resumed across versions.
  2. Update the server TLS stack to a version that correctly binds session version.
  3. Set Config.SessionTicketsDisabled = true on the client to bypass resumption if the server is unfixable.

Example fix

// Disable resumption when the server mishandles session versions
cfg := &tls.Config{SessionTicketsDisabled: true}
Defensive patterns

Strategy: fallback

Type guard

func isResumptionVersionMismatch(err error) bool {
    return err != nil && strings.Contains(err.Error(), "server resumed a session with a different version")
}

Try / catch

if _, err := tls.Dial("tcp", addr, cfg); err != nil {
    if isResumptionVersionMismatch(err) {
        // Retry with session resumption disabled; server ticket cache is inconsistent.
        cfg.SessionTicketsDisabled = true
        cfg.ClientSessionCache = nil
        _, err = tls.Dial("tcp", addr, cfg)
    }
}

Prevention

When it happens

Trigger: Server session cache returning a ticket/session keyed for TLS 1.3 while negotiating TLS 1.2 (or vice versa); server bug in ticket handling; downgrade attack between the original and resumed handshake.

Common situations: Misconfigured server ticket cache spanning versions; rare in mainstream servers.

Understand the failure class

Related errors


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