golang/go · error
tls: client sent encrypted_client_hello extension but did no
Error message
tls: client sent encrypted_client_hello extension but did not offer TLS 1.3
What it means
Thrown when the reconstructed ECH inner ClientHello's supported_versions extension does not include TLS 1.3 (0x0304) at all, yet no version below 1.3 was found either (otherwise error 568 would have fired first). This means all entries are GREASE values or hypothetical future versions above 1.3, with TLS 1.3 itself absent. A valid ECH client must offer TLS 1.3.
Source
Thrown at src/crypto/tls/ech.go:398
// 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) // outer
b.AddUint16(kdfID)
b.AddUint16(aeadID)
b.AddUint8(id)
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { b.AddBytes(encodedKey) })
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { b.AddBytes(payload) })View on GitHub (pinned to b6b368adc5)
Solutions
- Ensure the ECH client's inner ClientHello includes TLS 1.3 (0x0304) in supported_versions
- Verify the client's tls.Config includes VersionTLS13 in its supported versions
- Check for decryption key mismatches that could corrupt the supported_versions extension data
- Update the client ECH library to ensure it always offers TLS 1.3
Defensive patterns
Strategy: validation
Try / catch
// Server-side: wrapped into errInvalidECHExt. // This is a client-side encoding issue; the server cannot prevent it.
Prevention
- Ensure the ECH client always includes TLS 1.3 in supported_versions of the inner hello
- Verify the client's supported versions list includes VersionTLS13
- Test ECH clients against reference servers to catch version omission bugs
When it happens
Trigger: The inner ClientHello's supported_versions extension contains only GREASE values (0x?A0A) and/or versions above TLS 1.3, with VersionTLS13 (0x0304) absent from the list.
Common situations: A malformed supported_versions extension in the inner hello that omits TLS 1.3. A client using an experimental future TLS version without including 1.3 as a fallback. Decryption corruption that alters the supported_versions extension data.
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 with unsup
- 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/615d3d6ebad4c16f.
Report an issue: GitHub.