{"record":{"id":"15caea5cb64e3eb8","repo":"golang/go","slug":"tls-no-key-exchanges-supported-by-both-client-and","errorCode":null,"errorMessage":"tls: no key exchanges supported by both client and server","messagePattern":"tls: no key exchanges supported by both client and server","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_server_tls13.go","lineNumber":215,"sourceCode":"\tc.cipherSuite = hs.suite.id\n\ths.hello.cipherSuite = hs.suite.id\n\ths.transcript = hs.suite.hash.New()\n\n\t// First, if a post-quantum key exchange is available, use one. See\n\t// draft-ietf-tls-key-share-prediction-01, Section 4 for why this must be\n\t// first.\n\t//\n\t// Second, if the client sent a key share for a group we support, use that,\n\t// to avoid a HelloRetryRequest round-trip.\n\t//\n\t// Finally, pick in our fixed preference order.\n\tpreferredGroups := c.config.curvePreferences(c.vers)\n\tpreferredGroups = slices.DeleteFunc(preferredGroups, func(group CurveID) bool {\n\t\treturn !slices.Contains(hs.clientHello.supportedCurves, group)\n\t})\n\tif len(preferredGroups) == 0 {\n\t\tc.sendAlert(alertHandshakeFailure)\n\t\treturn errors.New(\"tls: no key exchanges supported by both client and server\")\n\t}\n\thasKeyShare := func(group CurveID) bool {\n\t\tfor _, ks := range hs.clientHello.keyShares {\n\t\t\tif ks.group == group {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\tsort.SliceStable(preferredGroups, func(i, j int) bool {\n\t\treturn hasKeyShare(preferredGroups[i]) && !hasKeyShare(preferredGroups[j])\n\t})\n\tsort.SliceStable(preferredGroups, func(i, j int) bool {\n\t\treturn isPQKeyExchange(preferredGroups[i]) && !isPQKeyExchange(preferredGroups[j])\n\t})\n\tselectedGroup := preferredGroups[0]\n\n\tvar clientKeyShare *keyShare","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_server_tls13.go#L197-L233","documentation":"After intersecting the server's curvePreferences with the client's supported_groups (hs.clientHello.supportedCurves), the result is empty — no (EC)DHE group is mutually supported. Per RFC 8446 §4.2.7 the server sends handshake_failure and aborts.","triggerScenarios":"Server's tls.Config.CurvePreferences filtered against the client's supportedCurves yields zero groups. E.g., server configured CurvePreferences = [X25519] but client only offers P-256; or vice-versa.","commonSituations":"Hardened/locked-down CurvePreferences; FIPS-only server configs (P-256/P-384) talking to clients that only offer modern curves; misconfigured load balancers that strip supported_groups; very old or very constrained clients.","solutions":["Widen tls.Config.CurvePreferences to include widely-supported groups (X25519, CurveP256, CurveP384)","If using defaults, the client offered zero recognized curves — update the client","For FIPS deployments, ensure the client offers at least one FIPS-approved group (P-256 or P-384)"],"exampleFix":"// before (too narrow)\ncfg := &tls.Config{CurvePreferences: []tls.CurveID{tls.CurveP521}}\n\n// after (broad interop)\ncfg := &tls.Config{CurvePreferences: []tls.CurveID{\n    tls.X25519, tls.CurveP256, tls.CurveP384, tls.CurveP521,\n}}","handlingStrategy":"validation","validationCode":"// Validate curve overlap before listening, against the groups you expect clients to offer.\ncommonCurves := []tls.CurveID{tls.X25519, tls.CurveP256, tls.CurveP384}\nif len(cfg.CurvePreferences) > 0 {\n    ok := false\n    for _, g := range commonCurves {\n        for _, c := range cfg.CurvePreferences {\n            if g == c { ok = true }\n        }\n    }\n    if !ok {\n        log.Printf(\"WARN: CurvePreferences %v may not intersect common client groups %v\", cfg.CurvePreferences, commonCurves)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := tlsConn.Handshake(); err != nil {\n    if strings.Contains(err.Error(), \"no key exchanges supported\") {\n        log.Printf(\"no common group with client %v; check CurvePreferences\", remote)\n    }\n    c.Close()\n    return\n}","preventionTips":["Keep CurvePreferences broad (X25519, P-256, P-384) for interop unless you have a hardening reason","In FIPS mode, document that clients must offer P-256 or P-384","Audit load balancers for supported_groups stripping"],"tags":["tls","go","key-exchange","ecdhe","curve-preferences","fips","handshake"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}