golang/go · error
tls: server selected an invalid PSK
Error message
tls: server selected an invalid PSK
What it means
Server selected a PSK identity index beyond what the client offered. RFC 8446 §4.2.11 requires the server's `pre_shared_key` identity index to be valid; Go checks `selectedIdentity < len(hs.hello.pskIdentities)` and sends `illegal_parameter` on violation. Indicates a server misuse of session resumption.
Source
Thrown at src/crypto/tls/handshake_client_tls13.go:447
if hs.serverHello.serverShare.group == 0 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server did not send a key share")
}
if !slices.ContainsFunc(hs.hello.keyShares, func(ks keyShare) bool {
return ks.group == hs.serverHello.serverShare.group
}) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server selected unsupported group")
}
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.verifiedChainsView on GitHub (pinned to b6b368adc5)
Solutions
- Report to the server operator — selected PSK index must be < number of client-offered identities.
- Disable session resumption on the client (SetSessionCache(nil) and ClientSessionCache nil) to sidestep PSK negotiation while diagnosing.
- Confirm the ClientHello actually carried the PSK identities you expected.
- Check the server's ticket key rotation logic if you operate it.
Example fix
// before: client resumption configured but server mishandles identity index
cfg := &tls.Config{ClientSessionCache: tls.NewLRUClientSessionCache(10)}
// after: temporarily disable resumption to confirm the cause
cfg := &tls.Config{ClientSessionCache: nil} Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: if you disable resumption you cannot hit out-of-range PSK selection.
if diagnosticallyResumptionBuggy {
cfg.ClientSessionCache = nil
} Try / catch
if err := conn.Handshake(); err != nil {
if strings.Contains(err.Error(), "invalid PSK") {
// drop any cached ticket and retry once with a fresh handshake
if cfg.ClientSessionCache != nil { cfg.ClientSessionCache = nil }
}
} Prevention
- Clear the ClientSessionCache after server config changes.
- Do not share a session cache across connections to differently-configured servers.
- Log PSK-related failures separately to spot server-side resumption bugs.
When it happens
Trigger: ServerHello.selectedIdentityPresent is true and selectedIdentity >= the number of PSK identities the client sent. Reached whenever the server acknowledges a PSK the client did not offer.
Common situations: Buggy server resumption logic, mismatched session ticket caches, a server cluster with non-shared ticket keys selecting an out-of-range index, or fuzz/adversarial traffic.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: server selected an invalid PSK and cipher suite pair
- 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/03db98b9427c4710.
Report an issue: GitHub.