{"record":{"id":"276effeebf1c5d93","repo":"golang/go","slug":"tls-no-supported-key-exchange-methods-curveids","errorCode":null,"errorMessage":"tls: no supported key exchange methods (CurveIDs)","messagePattern":"tls: no supported key exchange methods \\(CurveIDs\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_client.go","lineNumber":143,"sourceCode":"\t}\n\n\tvar keyShareKeys *keySharePrivateKeys\n\tif maxVersion >= VersionTLS13 {\n\t\t// Reset the list of ciphers when the client only supports TLS 1.3.\n\t\tif minVersion >= VersionTLS13 {\n\t\t\thello.cipherSuites = nil\n\t\t}\n\n\t\tif fips140tls.Required() {\n\t\t\thello.cipherSuites = append(hello.cipherSuites, allowedCipherSuitesTLS13FIPS...)\n\t\t} else if hasAESGCMHardwareSupport {\n\t\t\thello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...)\n\t\t} else {\n\t\t\thello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...)\n\t\t}\n\n\t\tif len(hello.supportedCurves) == 0 {\n\t\t\treturn nil, nil, nil, errors.New(\"tls: no supported key exchange methods (CurveIDs)\")\n\t\t}\n\t\t// Since the order is fixed, the first one is always the one to send a\n\t\t// key share for. All the PQ hybrids sort first, and produce a fallback\n\t\t// ECDH share.\n\t\tcurveID := hello.supportedCurves[0]\n\t\tke, err := keyExchangeForCurveID(curveID)\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, errors.New(\"tls: internal error: supportsCurve accepted unimplemented curve\")\n\t\t}\n\t\tkeyShareKeys, hello.keyShares, err = ke.keyShares(config.rand())\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, err\n\t\t}\n\t\t// Only send the fallback ECDH share if the corresponding CurveID is enabled.\n\t\tif len(hello.keyShares) == 2 && !slices.Contains(hello.supportedCurves, hello.keyShares[1].group) {\n\t\t\thello.keyShares = hello.keyShares[:1]\n\t\t}\n\t}","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_client.go#L125-L161","documentation":"Thrown by makeClientHello when TLS 1.3 is being negotiated (maxVersion >= VersionTLS13) but hello.supportedCurves is empty after filtering. The supportedCurves list is derived from config.curvePreferences(maxVersion), which filters the library's known curve set by the config's CurvePreferences and FIPS constraints. An empty result means no usable key exchange curve is available for TLS 1.3, which requires at least one named group for key exchange.","triggerScenarios":"Setting config.CurvePreferences to a non-empty slice containing only curve IDs that are unknown to the library (all filtered out by supportsCurve). Enabling FIPS mode (fips140tls.Required()) while CurvePreferences contains only curves not in the FIPS-allowed set. A GODEBUG setting that disables all default curves.","commonSituations":"Setting CurvePreferences to experimental or unsupported curve IDs. FIPS mode filtering out all configured curves (e.g., X25519 may not be FIPS-approved depending on the module). A GODEBUG flag like x25519mul=0 or similar that disables a curve at runtime, combined with a CurvePreferences list that only includes that curve.","solutions":["Include at least one standard, library-supported curve in CurvePreferences (e.g., tls.X25519, tls.CurveP256, tls.CurveP384)","Leave CurvePreferences unset (nil) to use the library default curve set","If running in FIPS mode, ensure CurvePreferences includes at least one FIPS-approved curve (P-256, P-384)","Check GODEBUG settings that might disable specific curves at runtime"],"exampleFix":"// before — only unsupported curves\nconfig := &tls.Config{\n    CurvePreferences: []tls.CurveID{999, 998}, // unknown curve IDs\n}\n// after\nconfig := &tls.Config{\n    CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256, tls.CurveP384},\n}\n// or omit entirely:\nconfig := &tls.Config{}","handlingStrategy":"validation","validationCode":"func validateCurvePreferences(config *tls.Config) error {\n    if len(config.CurvePreferences) == 0 {\n        return nil // nil/empty uses library defaults — always valid\n    }\n    knownCurves := []tls.CurveID{\n        tls.X25519, tls.CurveP256, tls.CurveP384, tls.CurveP521,\n    }\n    hasKnown := false\n    for _, c := range config.CurvePreferences {\n        for _, k := range knownCurves {\n            if c == k {\n                hasKnown = true\n                break\n            }\n        }\n    }\n    if !hasKnown {\n        return errors.New(\"CurvePreferences contains no library-supported curves\")\n    }\n    return nil\n}","typeGuard":"// Check if a CurveID is a standard supported curve\nfunc isStandardCurve(c tls.CurveID) bool {\n    switch c {\n    case tls.X25519, tls.CurveP256, tls.CurveP384, tls.CurveP521:\n        return true\n    }\n    return false\n}","tryCatchPattern":"// Pre-validate before dial:\n//\n//   if err := validateCurvePreferences(config); err != nil {\n//       config.CurvePreferences = nil // reset to defaults\n//   }","preventionTips":["Leave CurvePreferences unset (nil) to use library defaults","Only include standard, well-known curves (X25519, P-256, P-384) in CurvePreferences","For FIPS mode, ensure at least one FIPS-approved curve (P-256, P-384) is configured","Avoid setting CurvePreferences to experimental or unknown curve IDs"],"tags":["tls","client-side","config","key-exchange","tls13","curves","fips"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}