golang/go · error
tls: server selected unsupported curve
Error message
tls: server selected unsupported curve
What it means
During TLS 1.0–1.2 ECDHE key exchange on the client side, the server's ServerKeyExchange message specified a curve type other than 'named_curve' (3). The first byte of the ECDHE params (skx.key[0]) must be 3 per RFC 4492/8422; any other value (e.g. 1=explicit_prime or 2=explicit_char2) is rejected because Go's crypto/tls only supports named curves.
Source
Thrown at src/crypto/tls/key_agreement.go:276
peerKey, err := ka.key.Curve().NewPublicKey(ckx.ciphertext[1:])
if err != nil {
return nil, errClientKeyExchange
}
preMasterSecret, err := ka.key.ECDH(peerKey)
if err != nil {
return nil, errClientKeyExchange
}
return preMasterSecret, nil
}
func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
if len(skx.key) < 4 {
return errServerKeyExchange
}
if skx.key[0] != 3 { // named curve
return errors.New("tls: server selected unsupported curve")
}
ka.curveID = CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
publicLen := int(skx.key[3])
if publicLen+4 > len(skx.key) {
return errServerKeyExchange
}
serverECDHEParams := skx.key[:4+publicLen]
publicKey := serverECDHEParams[4:]
sig := skx.key[4+publicLen:]
if len(sig) < 2 {
return errServerKeyExchange
}
if ka.version >= VersionTLS12 {
ka.signatureAlgorithm = SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
sig = sig[2:]
if len(sig) < 2 {View on GitHub (pinned to b6b368adc5)
Solutions
- Update the server to a modern TLS library that uses named curves (curve type 3).
- Verify no intermediary is modifying the ServerKeyExchange message.
- If you control the server, ensure it only sends named_curve ECDHE parameters.
- Capture the ServerKeyExchange with Wireshark and check the ECParameters curve_type field.
- Use TLS 1.3 which eliminates this legacy parameter format.
Defensive patterns
Strategy: try-catch
Validate before calling
// Client-side: no pre-check is possible since the server controls ServerKeyExchange.
// You can restrict cipher suites to avoid ECDHE with non-conformant servers:
// Prefer TLS 1.3 which doesn't have this format issue.
func preferTLS13(cfg *tls.Config) {
cfg.MinVersion = tls.VersionTLS13
} Try / catch
// Client-side: handle during handshake
if err := conn.Handshake(); err != nil {
if strings.Contains(err.Error(), "server selected unsupported curve") {
log.Printf("server sent non-named-curve ECDHE params: %v", err)
// Server is non-conformant or very old — upgrade or avoid
}
} Prevention
- Prefer TLS 1.3 which eliminates legacy ECDHE parameter formats.
- Avoid TLS 1.0/1.1 connections to very old servers.
- Test against modern servers that use named curves.
- Report non-conformant servers to their maintainers.
When it happens
Trigger: Client calls ecdheKeyAgreement.processServerKeyExchange and the first byte of skx.key is not 3. The server sent an explicit curve parameter set (deprecated in RFC 8422) instead of a named curve ID.
Common situations: Server uses a very old or non-conformant TLS library that sends explicit curve parameters; a custom server implementation that doesn't use named curves; a MITM altering the ServerKeyExchange; testing against an old server that predates RFC 8422 deprecation of explicit curves.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: unexpected ServerKeyExchange
- tls: no supported elliptic curves offered
- tls: no supported key exchange methods (CurveIDs)
- tls: server certificate contains incorrect key type for sele
- tls: internal error: supportsCurve accepted unimplemented cu
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/615e0739e3b5b046.
Report an issue: GitHub.