golang/go · error
tls: server selected an invalid PSK and cipher suite pair
Error message
tls: server selected an invalid PSK and cipher suite pair
What it means
Server selected a PSK whose original cipher suite hash does not match the hash of the cipher suite negotiated for this handshake. RFC 8446 §4.2.11 mandates the binder hash be consistent. Go sends `illegal_parameter`. Indicates inconsistent session resumption bookkeeping.
Source
Thrown at src/crypto/tls/handshake_client_tls13.go:459
if !hs.serverHello.selectedIdentityPresent {
return nil
}
if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server selected an invalid PSK")
}
if len(hs.hello.pskIdentities) != 1 || hs.session == nil {
return c.sendAlert(alertInternalError)
}
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
if pskSuite == nil {
return c.sendAlert(alertInternalError)
}
if pskSuite.hash != hs.suite.hash {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server selected an invalid PSK and cipher suite pair")
}
hs.usingPSK = true
c.didResume = true
c.peerCertificates = hs.session.peerCertificates
c.verifiedChains = hs.session.verifiedChains
c.ocspResponse = hs.session.ocspResponse
c.scts = hs.session.scts
return nil
}
func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
c := hs.c
ke, err := keyExchangeForCurveID(hs.serverHello.serverShare.group)
if err != nil {
c.sendAlert(alertInternalError)
return errView on GitHub (pinned to b6b368adc5)
Solutions
- Clear the client session cache (discard stale tickets) and retry the handshake fresh.
- Align server-side cipher suite preferences so resumed and initial handshakes use the same suite family.
- Disable 0-RTT/resumption temporarily to confirm the cause.
- Report to the server operator if it persists across fresh sessions.
Example fix
// before: stale tickets from a cipher-suite change cause mismatch
cfg := &tls.Config{ClientSessionCache: tls.NewLRUClientSessionCache(100)}
// after: flush cache so a fresh ticket is minted under the current suite
cfg := &tls.Config{ClientSessionCache: tls.NewLRUClientSessionCache(0)} Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: pin cipher suites so resumed and initial handshakes use the same family.
cfg.CipherSuites = []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
} Try / catch
if err := conn.Handshake(); err != nil {
if strings.Contains(err.Error(), "invalid PSK and cipher suite pair") {
cfg.ClientSessionCache = nil // stale ticket; retry fresh
return retryHandshake(addr, cfg)
}
} Prevention
- Pin CipherSuites on both client and server so resumption is consistent.
- Clear the session cache whenever cipher suite preferences change.
- Avoid mixing TLS 1.3 sessions across load-balanced backends with divergent configs.
When it happens
Trigger: selectedIdentity is in range and the resumed session's cipher suite (looked up via cipherSuiteTLS13ByID) has a hash that differs from the negotiated suite's hash.
Common situations: Session ticket cached under one cipher suite then resumed under another (e.g., server changed its suite preferences), a server cluster with divergent configs, or a buggy server that ignores the resumption-cipher-suite binding.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: server selected an invalid PSK
- tls: server sent two HelloRetryRequest messages
- tls: server sent a cookie in a normal ServerHello
- tls: malformed key_share extension
- tls: server did not send a key share
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/498a1989ce5e42ba.
Report an issue: GitHub.