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

  1. Ensure the ECH client's inner ClientHello includes TLS 1.3 (0x0304) in supported_versions
  2. Verify the client's tls.Config includes VersionTLS13 in its supported versions
  3. Check for decryption key mismatches that could corrupt the supported_versions extension data
  4. 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

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

Related errors


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