gorilla/websocket · error

websocket: protocol %q was given but is not supported;sharin

Error message

websocket: protocol %q was given but is not supported;sharing tls.Config with net/http Transport can cause this error: %w

What it means

DialContext wraps the error from http.ReadResponse with this message when a TLS NextProtos entry other than "http/1.1" is configured. The library requires that the TLS config used for the WebSocket dial only negotiates HTTP/1.1; if an ALPN protocol like h2 is listed first, the TLS handshake may negotiate it and the expected HTTP/1.1 upgrade response never arrives, so the response read fails.

Source

Thrown at client.go:343

	conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil)

	if err := req.Write(netConn); err != nil {
		return nil, nil, err
	}

	if trace != nil && trace.GotFirstResponseByte != nil {
		if peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 {
			trace.GotFirstResponseByte()
		}
	}

	resp, err := http.ReadResponse(conn.br, req)
	if err != nil {
		if d.TLSClientConfig != nil {
			for _, proto := range d.TLSClientConfig.NextProtos {
				if proto != "http/1.1" {
					return nil, nil, fmt.Errorf(
						"websocket: protocol %q was given but is not supported;"+
							"sharing tls.Config with net/http Transport can cause this error: %w",
						proto, err,
					)
				}
			}
		}
		return nil, nil, err
	}

	if d.Jar != nil {
		if rc := resp.Cookies(); len(rc) > 0 {
			d.Jar.SetCookies(u, rc)
		}
	}

	if resp.StatusCode != 101 ||
		!tokenListContainsValue(resp.Header, "Upgrade", "websocket") ||

View on GitHub (pinned to e064f32e36)

Solutions

  1. Clone the tls.Config for the dialer and set NextProtos to []string{"http/1.1"} (or remove non-http/1.1 entries)
  2. Do not share a single tls.Config between net/http Transport and websocket.Dialer
  3. If you control the server, ensure it supports the ALPN fallback the client offers
  4. Inspect the inner error via errors.Unwrap to confirm the underlying response-read failure

Example fix

// before
dialer := websocket.Dialer{TLSClientConfig: sharedTLSConfig}
// after
cfg := sharedTLSConfig.Clone()
cfg.NextProtos = []string{"http/1.1"}
dialer := websocket.Dialer{TLSClientConfig: cfg}
Defensive patterns

Strategy: fallback

Validate before calling

func wsTLSConfig(base *tls.Config) *tls.Config {
    cfg := base.Clone()
    cfg.NextProtos = []string{"http/1.1"}
    return cfg
}

Try / catch

dialer := websocket.Dialer{TLSClientConfig: wsTLSConfig(sharedTLSConfig)}
_, resp, err := dialer.DialContext(ctx, url, nil)
if err != nil && strings.Contains(err.Error(), "was given but is not supported") {
    return fmt.Errorf("tls config shares ALPN protos with http transport: %w", err)
}

Prevention

When it happens

Trigger: Sharing a *tls.Config whose NextProtos contains e.g. "h2" with Dialer.TLSClientConfig (often the same config used by an http.Transport), then the ALPN negotiation picks a non-HTTP/1.1 protocol and http.ReadResponse fails, producing this wrapped error.

Common situations: Reusing an application-wide tls.Config from an HTTP/2-enabled net/http Transport, default NextProtos lists that include h2, or copying TLS config from an existing HTTP client into the WebSocket dialer.

Understand the failure class

Related errors


AI-assisted analysis of gorilla/websocket@e064f32e36 (2026-08-31). Data as JSON: /api/errors/506fe77f7ce567ae. Report an issue: GitHub.