golang/go · error

tls: server did not send a quic_transport_parameters extensi

Error message

tls: server did not send a quic_transport_parameters extension

What it means

QUIC-specific: RFC 9001 §8.2 mandates that a QUIC server's EncryptedExtensions carry the quic_transport_parameters extension. When the tls.Config is in QUIC mode (c.quic != nil) and the extension is absent, Go sends `missing_extension` and aborts. Indicates a QUIC server that did not echo transport parameters.

Source

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

		c.sendAlert(alertUnexpectedMessage)
		return unexpectedMessageError(encryptedExtensions, msg)
	}

	if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol, c.quic != nil); err != nil {
		// RFC 8446 specifies that no_application_protocol is sent by servers, but
		// does not specify how clients handle the selection of an incompatible protocol.
		// RFC 9001 Section 8.1 specifies that QUIC clients send no_application_protocol
		// in this case. Always sending no_application_protocol seems reasonable.
		c.sendAlert(alertNoApplicationProtocol)
		return err
	}
	c.clientProtocol = encryptedExtensions.alpnProtocol

	if c.quic != nil {
		if encryptedExtensions.quicTransportParameters == nil {
			// RFC 9001 Section 8.2.
			c.sendAlert(alertMissingExtension)
			return errors.New("tls: server did not send a quic_transport_parameters extension")
		}
		c.quicSetTransportParameters(encryptedExtensions.quicTransportParameters)
	} else {
		if encryptedExtensions.quicTransportParameters != nil {
			c.sendAlert(alertUnsupportedExtension)
			return errors.New("tls: server sent an unexpected quic_transport_parameters extension")
		}
	}

	if !hs.hello.earlyData && encryptedExtensions.earlyData {
		c.sendAlert(alertUnsupportedExtension)
		return errors.New("tls: server sent an unexpected early_data extension")
	}
	if hs.hello.earlyData && !encryptedExtensions.earlyData {
		c.quicRejectedEarlyData()
	}
	if encryptedExtensions.earlyData {
		if hs.session.cipherSuite != c.cipherSuite {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Confirm the peer is actually a QUIC server, not a plain TLS-over-TCP server.
  2. Update the QUIC library (quic-go et al.) and the server to matching versions.
  3. Verify QUIC transport parameters are sent by the client too (QUIC requires both directions).
  4. Report the missing extension to the QUIC server implementer.
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: confirm this is genuinely a QUIC connection and your QUIC lib is wired up.
// QUIC mode is entered by the QUIC library; if c.quic == nil you are NOT in QUIC mode.
if !usingQUIC {
    return errors.New("peer is a QUIC server; use a QUIC transport, not tls.Dial")
}

Try / catch

// QUIC handshake errors surface from the QUIC library wrapping crypto/tls.
// Check your QUIC library's error type (e.g., quic-go ApplicationError/TransportError).
if err := qconn.Handshake(); err != nil {
    var te *qerr.TransportError
    if errors.As(err, &te) { /* inspect te.ErrorCode */ }
}

Prevention

When it happens

Trigger: Calling tls.Config for a QUIC connection (config has QUIC transport parameters set via QUIC control-plane APIs) and the server's EncryptedExtensions omits quic_transport_parameters.

Common situations: QUIC server (HTTP/3, QUIC transport) that does not implement the TLS-QUIC integration correctly, version skew between the QUIC library and the server, or a server that is actually a plain TLS server reached over a QUIC tunnel.

Understand the failure class

Related errors


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