golang/go · error
tls: Encrypted Client Hello cannot be used pre-TLS 1.3
Error message
tls: Encrypted Client Hello cannot be used pre-TLS 1.3
What it means
The server negotiated a TLS version below 1.3 while observing an outer Encrypted Client Hello (ECH) extension. Per the ECH specification and BoringSSL behavior mirrored here, ECH is only valid with TLS 1.3; using outer ECH with an older version is illegal_parameter. Backend servers accepting inner ECH at 1.2 are tolerated, but outer ECH at < 1.3 is rejected.
Source
Thrown at src/crypto/tls/handshake_server.go:209
c.vers, ok = c.config.mutualVersion(roleServer, c.quic != nil, clientVersions)
if !ok {
c.sendAlert(alertProtocolVersion)
return nil, nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
}
c.haveVers = true
c.in.version = c.vers
c.out.version = c.vers
// This check reflects some odd specification implied behavior. Client-facing servers
// are supposed to reject hellos with outer ECH and inner ECH that offers 1.2, but
// backend servers are allowed to accept hellos with inner ECH that offer 1.2, since
// they cannot expect client-facing servers to behave properly. Since we act as both
// a client-facing and backend server, we only enforce 1.3 being negotiated if we
// saw a hello with outer ECH first. The spec probably should've made this an error,
// but it didn't, and this matches the boringssl behavior.
if c.vers != VersionTLS13 && (ech != nil && !ech.inner) {
c.sendAlert(alertIllegalParameter)
return nil, nil, errors.New("tls: Encrypted Client Hello cannot be used pre-TLS 1.3")
}
return clientHello, ech, nil
}
func (hs *serverHandshakeState) processClientHello() error {
c := hs.c
hs.hello = new(serverHelloMsg)
hs.hello.vers = c.vers
foundCompression := false
// We only support null compression, so check that the client offered it.
for _, compression := range hs.clientHello.compressionMethods {
if compression == compressionNone {
foundCompression = true
break
}View on GitHub (pinned to b6b368adc5)
Solutions
- Ensure the client's tls.Config does not cap MaxVersion below VersionTLS13 when ECH is enabled.
- Investigate middleboxes or proxies that may strip the TLS 1.3 supported_versions entry.
- Update the client TLS stack to a version that correctly pairs ECH with TLS 1.3 negotiation.
- If ECH is not required, disable it on the client to allow fallback to a standard 1.2 handshake.
Example fix
// before
cfg := &tls.Config{
MaxVersion: tls.VersionTLS12, // forces downgrade; ECH becomes illegal
// ECH configured via ECHConfig list
}
// after
cfg := &tls.Config{
MinVersion: tls.VersionTLS13,
MaxVersion: tls.VersionTLS13,
} Defensive patterns
Strategy: validation
Validate before calling
// Client: when enabling ECH, also require TLS 1.3.
if len(echConfigList) > 0 {
cfg.MinVersion = tls.VersionTLS13
cfg.MaxVersion = tls.VersionTLS13 // or omit, but never < 1.3
} Try / catch
if err != nil && strings.Contains(err.Error(), "Encrypted Client Hello cannot be used pre-TLS 1.3") {
cfg.MaxVersion = 0 // allow 1.3
// retry handshake
} Prevention
- Pair ECH configuration with MinVersion >= TLS 1.3.
- Audit middleboxes for supported_versions stripping.
- Document that ECH implies TLS 1.3 in your config.
When it happens
Trigger: In the server's clientHello processing, c.vers != VersionTLS13 and an ECH extension was decoded with ech.inner == false (i.e. the outer SNI was encrypted). The client offered ECH but negotiated an older version.
Common situations: A client that sends ECH but advertises max_version of TLS 1.2, a downgrade forced by a middlebox stripping supported_versions, or a misconfigured client library combining ECH with legacy version caps.
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: client sent encrypted_client_hello extension but did no
- tls: MinVersion must be >= VersionTLS13 if EncryptedClientHe
- tls: MaxVersion must be >= VersionTLS13 if EncryptedClientHe
- tls: server sent encrypted client hello retry configs after
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/013e4051fea7a63d.
Report an issue: GitHub.