golang/go · error

tls: invalid reconstructed inner client hello

Error message

tls: invalid reconstructed inner client hello

What it means

Thrown after ECH inner ClientHello reconstruction when the fully reassembled bytes (decrypted inner fields plus expanded outer extensions substituted in) fail to parse as a valid ClientHello via clientHelloMsg.unmarshal(). This is a comprehensive final validation that catches inconsistencies not detected by earlier field-level checks, such as duplicate extensions, invalid field values, or structural contradictions introduced during extension expansion.

Source

Thrown at src/crypto/tls/ech.go:370

						})
					}
				} else {
					recon.AddUint16(extension)
					recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
						recon.AddBytes(extData)
					})
				}
			}
		})
	})

	reconBytes, err := recon.Bytes()
	if err != nil {
		return nil, err
	}
	inner := &clientHelloMsg{}
	if !inner.unmarshal(reconBytes) {
		return nil, errors.New("tls: invalid reconstructed inner client hello")
	}

	if !bytes.Equal(inner.encryptedClientHello, []byte{uint8(innerECHExt)}) {
		return nil, errInvalidECHExt
	}

	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.

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Ensure the ECH client does not duplicate extensions between the inner-only set and the outer_extensions compressed reference
  2. Verify that all extension data in the outer hello is consistent with the inner hello's fields (e.g., supported_versions, key_share, etc.)
  3. Check for extension type collisions between inner and outer extension sets
  4. Test the client against a reference server implementation to identify reconstruction failures
Defensive patterns

Strategy: validation

Try / catch

// Wrapped into errInvalidECHExt by processECHClientHello.

Prevention

When it happens

Trigger: The reconstructed inner ClientHello — assembled from decrypted inner content with outer_extensions expanded to their full extension data from the outer hello — contains data that fails the complete ClientHello parser. Possible causes: duplicate extension types after expansion, invalid cipher suite values, unsupported version fields, or extension data that is individually valid but collectively contradictory.

Common situations: The expansion of outer extensions introduced a duplicate extension that also exists in the inner-only set. The outer hello's extension data is incompatible with the inner hello's structure. A client bug where the inner and outer hello share an extension with conflicting values. Decryption corruption that produces fields valid individually but invalid collectively.

Understand the failure class

Related errors


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