golang/go · error
tls: server resumed a session with a different EMS extension
Error message
tls: server resumed a session with a different EMS extension
What it means
Per RFC 7627 5.3, an Extended Master Secret session cannot be resumed as a non-EMS session and vice versa. If hs.session.extMasterSecret != hs.serverHello.extendedMasterSecret the resumption is rejected with alertHandshakeFailure, because EMS binds the master secret to the handshake transcript and downgrading it on resumption weakens that binding.
Source
Thrown at src/crypto/tls/handshake_client.go:949
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
c.ocspResponse = hs.session.ocspResponse
// Let the ServerHello SCTs override the session SCTs from the original
// connection, if any are provided.
if len(c.scts) == 0 && len(hs.session.scts) != 0 {
c.scts = hs.session.scts
}
c.curveID = hs.session.curveID
return true, nil
}
View on GitHub (pinned to b6b368adc5)
Solutions
- Ensure the server consistently enables Extended Master Secret for both the original and resumed handshake.
- Upgrade the server TLS stack.
- Disable client-side session resumption if the server is non-conformant.
Example fix
cfg := &tls.Config{SessionTicketsDisabled: true} Defensive patterns
Strategy: fallback
Type guard
func isResumptionEMSMismatch(err error) bool {
return err != nil && strings.Contains(err.Error(), "server resumed a session with a different EMS extension")
} Try / catch
if _, err := tls.Dial("tcp", addr, cfg); err != nil {
if isResumptionEMSMismatch(err) {
// Security-relevant: server is downgrading EMS on resumption. Disable resumption.
cfg.SessionTicketsDisabled = true
cfg.ClientSessionCache = nil
_, err = tls.Dial("tcp", addr, cfg)
}
} Prevention
- Ensure the server enables EMS consistently for both original and resumed handshakes.
- Treat EMS downgrade on resumption as a security signal.
- Disable resumption against non-conformant servers.
When it happens
Trigger: Server resuming a session while downgrading EMS (a known triple-handshake class of attack); server bug flipping the EMS flag on resumption; downgrade attack on the resumed connection.
Common situations: TLS interception or a buggy server attempting to resume EMS-protected sessions without EMS; security-relevant.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: FIPS 140-3 requires the use of Extended Master Secret
- tls: server resumed a session with a different version
- tls: server resumed a session with a different cipher suite
- tls: invalid outer extensions
- tls: malformed encrypted_client_hello extension
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/c77814c5c219a8be.
Report an issue: GitHub.