golang/go · error
tls: client sent encrypted_client_hello extension with unsup
Error message
tls: client sent encrypted_client_hello extension with unsupported versions
What it means
Thrown when the reconstructed ECH inner ClientHello's supported_versions extension contains a non-GREASE version below TLS 1.3 (0x0304). ECH is defined exclusively for TLS 1.3 and later per RFC 9460, so advertising TLS 1.2 (0x0303), TLS 1.1 (0x0302), TLS 1.0 (0x0301), or SSL 3.0 (0x0300) in the inner hello is a protocol violation. GREASE values (0x?A0A pattern) are skipped before this check.
Source
Thrown at src/crypto/tls/ech.go:393
}
hasTLS13 := false
for _, v := range inner.supportedVersions {
// Skip GREASE values (values of the form 0x?A0A).
// GREASE (Generate Random Extensions And Sustain Extensibility) is a mechanism used by
// browsers like Chrome to ensure TLS implementations correctly ignore unknown values.
// GREASE values follow a specific pattern: 0x?A0A, where ? can be any hex digit.
// These values should be ignored when processing supported TLS versions.
if v&0x0F0F == 0x0A0A && v&0xff == v>>8 {
continue
}
// Ensure at least TLS 1.3 is offered.
if v == VersionTLS13 {
hasTLS13 = true
} else if v < VersionTLS13 {
// Reject if any non-GREASE value is below TLS 1.3, as ECH requires TLS 1.3+.
return nil, errors.New("tls: client sent encrypted_client_hello extension with unsupported versions")
}
}
if !hasTLS13 {
return nil, errors.New("tls: client sent encrypted_client_hello extension but did not offer TLS 1.3")
}
return inner, nil
}
func decryptECHPayload(context *hpke.Recipient, hello, payload []byte) ([]byte, error) {
outerAAD := bytes.Replace(hello[4:], payload, make([]byte, len(payload)), 1)
return context.Open(outerAAD, payload)
}
func generateOuterECHExt(id uint8, kdfID, aeadID uint16, encodedKey []byte, payload []byte) ([]byte, error) {
var b cryptobyte.Builder
b.AddUint8(0) // outerView on GitHub (pinned to b6b368adc5)
Solutions
- Set the client's MinVersion to at least tls.VersionTLS13 when using ECH
- Ensure the inner ClientHello's supported_versions extension only includes TLS 1.3 (0x0304) and optionally GREASE values
- If using a non-Go ECH client, verify it enforces the TLS 1.3 minimum for ECH inner hellos
- Remove any explicit version configuration that allows TLS 1.2 or earlier when ECH is enabled
Example fix
// before
config := &tls.Config{
EncryptedClientHelloConfigList: echConfigList,
MinVersion: tls.VersionTLS12, // allows TLS 1.2 — invalid with ECH
}
// after
config := &tls.Config{
EncryptedClientHelloConfigList: echConfigList,
MinVersion: tls.VersionTLS13,
} Defensive patterns
Strategy: validation
Validate before calling
// Client-side: verify MinVersion before enabling ECH
func validateECHVersionConfig(config *tls.Config) error {
if config.EncryptedClientHelloConfigList != nil {
if config.MinVersion != 0 && config.MinVersion < tls.VersionTLS13 {
return fmt.Errorf("MinVersion must be >= TLS 1.3 when ECH is enabled")
}
}
return nil
} Try / catch
// Server-side: wrapped into errInvalidECHExt.
// Client-side: prevent by validating config before dial:
//
// if err := validateECHVersionConfig(config); err != nil {
// log.Fatal(err)
// } Prevention
- Always set MinVersion >= tls.VersionTLS13 when using EncryptedClientHelloConfigList
- Validate the tls.Config in a constructor function before passing it to tls.Dial
- Write unit tests that assert ECH configs require TLS 1.3
When it happens
Trigger: The inner ClientHello's supported_versions extension includes at least one non-GREASE version ID with a numeric value less than 0x0304 (VersionTLS13).
Common situations: Client configured with MinVersion below TLS 1.3 while using ECH — though the client-side makeClientHello also checks this, a custom or non-Go client might not. An ECH client library that does not enforce the TLS 1.3 minimum for the inner hello. A misconfigured client that includes legacy versions in the inner hello's supported_versions.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: client sent encrypted_client_hello extension but did no
- tls: invalid outer extensions
- tls: MinVersion must be >= VersionTLS13 if EncryptedClientHe
- tls: MaxVersion must be >= VersionTLS13 if EncryptedClientHe
- tls: Encrypted Client Hello cannot be used pre-TLS 1.3
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/ade8d9249f198a9c.
Report an issue: GitHub.