golang/go · error

tls: unexpected encrypted client hello extension in serverHe

Error message

tls: unexpected encrypted client hello extension in serverHello

What it means

Thrown during HelloRetryRequest processing when the server includes an encrypted_client_hello extension in the ServerHello but the client has no ECH context (hs.echContext is nil). This means the client did not send ECH, so the server's ECH extension is unsolicited.

Source

Thrown at src/crypto/tls/handshake_client_tls13.go:289

				c.sendAlert(alertInternalError)
				return err
			}
			acceptConfirmation := tls13.ExpandLabel(h, prk, "hrr ech accept confirmation", confTranscript.Sum(nil), 8)
			if subtle.ConstantTimeCompare(acceptConfirmation, hs.serverHello.encryptedClientHello) == 1 {
				hello = hs.echContext.innerHello
				c.serverName = c.config.ServerName
				isInnerHello = true
				c.echAccepted = true
			}
		}

		if err := transcriptMsg(hs.serverHello, hs.echContext.innerTranscript); err != nil {
			return err
		}
	} else if hs.serverHello.encryptedClientHello != nil {
		// Unsolicited ECH extension should be rejected
		c.sendAlert(alertUnsupportedExtension)
		return errors.New("tls: unexpected encrypted client hello extension in serverHello")
	}

	// The only HelloRetryRequest extensions we support are key_share and
	// cookie, and clients must abort the handshake if the HRR would not result
	// in any change in the ClientHello.
	if hs.serverHello.selectedGroup == 0 && hs.serverHello.cookie == nil {
		c.sendAlert(alertIllegalParameter)
		return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
	}

	if hs.serverHello.cookie != nil {
		hello.cookie = hs.serverHello.cookie
	}

	if hs.serverHello.serverShare.group != 0 {
		c.sendAlert(alertDecodeError)
		return errors.New("tls: received malformed key_share extension")
	}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. If you want ECH support, set config.EncryptedClientHelloConfigList with valid ECH configuration records from DNS HTTPS/SVCB records.
  2. If you do not need ECH, this is a server bug — the server should not send an unsolicited encrypted_client_hello extension.
  3. Report the issue to the server operator with details of the unexpected extension.
  4. Test with ECH disabled on a known-good server to rule out client-side issues.

Example fix

// before — no ECH config but server expects it
config := &tls.Config{
    ServerName: "example.com",
}

// after — provide ECH config list from DNS HTTPS records
config := &tls.Config{
    ServerName:                      "example.com",
    EncryptedClientHelloConfigList: echConfigList, // from DNS HTTPS RR
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate ECH context before connecting
func validateECHPresence(config *tls.Config) error {
    // If the server is known to expect ECH, ensure the config list is set
    if config.EncryptedClientHelloConfigList == nil {
        // Not an error per se, but warn if connecting to an ECH-capable server
        log.Println("warning: no ECH config list set; servers expecting ECH will fail")
    }
    return nil
}

Try / catch

conn, err := tls.Dial("tcp", addr, config)
if err != nil {
    if strings.Contains(err.Error(), "unexpected encrypted client hello extension in serverHello") {
        // Server sent unsolicited ECH — this is a server bug, nothing the client can fix
        log.Printf("server sent unsolicited ECH extension: %v", err)
    }
}

Prevention

When it happens

Trigger: Triggered in the else-if branch when hs.echContext is nil and hs.serverHello.encryptedClientHello is not nil. The client sends alertUnsupportedExtension.

Common situations: Server sending an ECH extension without the client having requested it. Client config does not set EncryptedClientHelloConfigList but the server responds with ECH. Server bug or misconfiguration sending unsolicited ECH extension.

Understand the failure class

Related errors


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