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
- Clone the tls.Config for the dialer and set NextProtos to []string{"http/1.1"} (or remove non-http/1.1 entries)
- Do not share a single tls.Config between net/http Transport and websocket.Dialer
- If you control the server, ensure it supports the ALPN fallback the client offers
- 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
- Never share a tls.Config between http.Transport and websocket.Dialer — always Clone
- Force NextProtos to ["http/1.1"] for websocket dials
- Keep HTTP/2 ALPN config confined to your HTTP client setup
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- websocket: duplicate header not allowed:
- websocket: bad handshake
- websocket: invalid compression negotiation
- malformed ws or wss URL
- websocket: internal error, unexpected bytes at end of flate
AI-assisted analysis of gorilla/websocket@e064f32e36 (2026-08-31).
Data as JSON: /api/errors/506fe77f7ce567ae.
Report an issue: GitHub.