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.verifiedChains

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Report to the server operator — selected PSK index must be < number of client-offered identities.
  2. Disable session resumption on the client (SetSessionCache(nil) and ClientSessionCache nil) to sidestep PSK negotiation while diagnosing.
  3. Confirm the ClientHello actually carried the PSK identities you expected.
  4. 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

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

Related errors


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