golang/go · warning

client doesn't support certificate curve

Error message

client doesn't support certificate curve

What it means

Thrown by SupportsCertificate when the certificate is an ECDSA key on a curve (P-256/P-384/P-521) that does not appear in the client's SupportedCurves (or the server does not support that curve for the negotiated version). Without a shared curve the ECDSA cipher suite cannot be selected, so the cert is unusable for this client.

Source

Thrown at src/crypto/tls/common.go:1499

			switch pub.Curve {
			case elliptic.P256():
				curve = CurveP256
			case elliptic.P384():
				curve = CurveP384
			case elliptic.P521():
				curve = CurveP521
			default:
				return supportsRSAFallback(unsupportedCertificateError(c))
			}
			var curveOk bool
			for _, c := range chi.SupportedCurves {
				if c == curve && config.supportsCurve(vers, c) {
					curveOk = true
					break
				}
			}
			if !curveOk {
				return errors.New("client doesn't support certificate curve")
			}
			ecdsaCipherSuite = true
		case ed25519.PublicKey:
			if vers < VersionTLS12 || len(chi.SignatureSchemes) == 0 {
				return errors.New("connection doesn't support Ed25519")
			}
			ecdsaCipherSuite = true
		case *mldsa.PublicKey:
			// ML-DSA requires TLS 1.3, which we already excluded above.
			return errors.New("connection doesn't support ML-DSA")
		case *rsa.PublicKey:
		default:
			return supportsRSAFallback(unsupportedCertificateError(c))
		}
	} else {
		return supportsRSAFallback(unsupportedCertificateError(c))
	}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Generate or select an ECDSA cert on a curve the client advertises (P-256 is most widely supported).
  2. Set Config.CurvePreferences to include the cert's curve and ensure the client offers it.
  3. Provide multiple ECDSA certs on different curves and pick via GetCertificate using chi.SupportedCurves.
  4. Fall back to an RSA or Ed25519 cert when no ECDSA curve overlaps.

Example fix

// before: cert on P-521, client offers only P-256
cfg.Certificates = []tls.Certificate{p521Cert}

// after: add a P-256 cert and select by client curves
cfg.Certificates = []tls.Certificate{p256Cert, p521Cert}
cfg.GetCertificate = func(chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
    for _, c := range chi.SupportedCurves {
        if c == tls.CurveP521 { return &p521Cert, nil }
    }
    return &p256Cert, nil
}
Defensive patterns

Strategy: fallback

Validate before calling

// Pick an ECDSA cert whose curve the client advertises.
func pickECDSACert(chi *tls.ClientHelloInfo, byCurve map[tls.CurveID]*tls.Certificate) (*tls.Certificate, error) {
    for _, c := range chi.SupportedCurves {
        if cert, ok := byCurve[c]; ok { return cert, nil }
    }
    return nil, errors.New("client shares no ECDSA curve")
}

Prevention

When it happens

Trigger: Server has an ECDSA cert (e.g., P-384) but the client's SupportedCurves omits that curve, or config.supportsCurve returns false for it. Reached in the ECDSA branch of SupportsCertificate after curveOk stays false.

Common situations: Cert generated on P-384/P-521 but client only offers P-256/X25519; client stripped uncommon curves for policy; multi-cert setup picking the wrong cert for the client; curve disabled by CurvePreferences.

Understand the failure class

Related errors


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