golang/go · error
tls: internal error: supportsCurve accepted unimplemented cu
Error message
tls: internal error: supportsCurve accepted unimplemented curve
What it means
Internal invariant violation in the Go crypto/tls package. The local `supportsCurve` check accepted a curve ID as supported, but `keyExchangeForCurveID` had no implementation for it. It is sent with an `internal_error` alert. This should never happen with an unmodified Go toolchain; encountering it implies a stdlib bug or a fork that added a CurveID without registering its key exchange.
Source
Thrown at src/crypto/tls/handshake_client_tls13.go:326
// If the server sent a key_share extension selecting a group, ensure it's
// a group we advertised but did not send a key share for, and send a key
// share for it this time.
if curveID := hs.serverHello.selectedGroup; curveID != 0 {
if !slices.Contains(hello.supportedCurves, curveID) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server selected unsupported group")
}
if slices.ContainsFunc(hs.hello.keyShares, func(ks keyShare) bool {
return ks.group == curveID
}) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
}
ke, err := keyExchangeForCurveID(curveID)
if err != nil {
c.sendAlert(alertInternalError)
return errors.New("tls: internal error: supportsCurve accepted unimplemented curve")
}
hs.keyShareKeys, hello.keyShares, err = ke.keyShares(c.config.rand())
if err != nil {
c.sendAlert(alertInternalError)
return err
}
// Do not send the fallback ECDH key share in a HRR response.
hello.keyShares = hello.keyShares[:1]
}
if len(hello.pskIdentities) > 0 {
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
if pskSuite == nil {
return c.sendAlert(alertInternalError)
}
if pskSuite.hash == hs.suite.hash {
// Update binders and obfuscated_ticket_age.
ticketAge := c.config.time().Sub(time.Unix(int64(hs.session.createdAt), 0))View on GitHub (pinned to b6b368adc5)
Solutions
- Upgrade to the latest stable Go toolchain; check the release notes for crypto/tls regressions.
- File an issue at golang.org/issue with the Go version and the curve ID; this is a stdlib defect.
- If running a fork (BoringCrypto/FIPS vendor branch), rebuild against the upstream matching version.
- Temporarily restrict CurvePreferences to widely-supported groups (X25519, CurveP256) to dodge the unimplemented curve.
Example fix
// before: rely on default curve set (may include an unimplemented entry on a forked build)
cfg := &tls.Config{}
// after: pin to well-supported curves only
cfg := &tls.Config{CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256}} Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: ensure every curve in CurvePreferences has a key-exchange impl in this build.
// There is no public API, so restrict to curves you know are implemented.
allowed := []tls.CurveID{tls.X25519, tls.CurveP256, tls.CurveP384, tls.CurveP521}
for _, c := range cfg.CurvePreferences {
if !slices.Contains(allowed, c) {
return fmt.Errorf("CurvePreferences contains unsupported curve %d; remove it", c)
}
} Try / catch
// This is a stdlib invariant violation; surface it loudly, do not retry.
if err := conn.Handshake(); err != nil {
if strings.Contains(err.Error(), "supportsCurve accepted unimplemented curve") {
log.Printf("FATAL: stdlib bug or forked build; Go version %s", runtime.Version())
}
return err
} Prevention
- Pin CurvePreferences to widely-supported groups rather than relying on defaults.
- Track the Go version in deployment metadata so stdlib regressions are easy to correlate.
- Avoid forked/FIPS builds that add CurveID constants without key-exchange implementations.
When it happens
Trigger: Reached when the server's HelloRetryRequest selects a group that passed the supportedCurves Contains check (so Go thinks it supports it) but for which keyExchangeForCurveID returns an error. Only a Go stdlib regression or a custom build adding a CurveID without an implementation hits this.
Common situations: Running a patched/forked Go runtime, a broken FIPS or BoringCrypto build, or a future Go version where a curve constant was added but its KE function was not. Stock upstream Go should be reported as a bug.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: ML-DSA certificates require TLS 1.3
- tls: server sent an unnecessary HelloRetryRequest key_share
- ECDSA verification failure
- Ed25519 verification failure
- tls: missing signature_algorithms from TLS 1.2 peer
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/1cd01e691728b3b2.
Report an issue: GitHub.