txthinking/brook · error

string(b)

Error message

string(b)

What it means

WebSocketDial performs a WebSocket handshake and expects 'HTTP/1.1 101 Switching Protocols'. If the response starts with 'HTTP/1.1 ' but does not contain '101', the server refused the upgrade; the connection is closed and the full response is returned as the error message. The raw response is surfaced so the caller can see the server's status/reason.

Source

Thrown at websocket.go:156

	if _, err := c.Write(b); err != nil {
		c.Close()
		return nil, err
	}
	r := bufio.NewReader(c)
	for {
		b, err = r.ReadBytes('\n')
		if err != nil {
			c.Close()
			return nil, err
		}
		b = bytes.TrimSpace(b)
		if len(b) == 0 {
			break
		}
		if bytes.HasPrefix(b, []byte("HTTP/1.1 ")) {
			if !bytes.Contains(b, []byte("101")) {
				c.Close()
				return nil, errors.New(string(b))
			}
		}
		if bytes.HasPrefix(b, []byte("Sec-WebSocket-Accept: ")) {
			h := sha1.New()
			h.Write([]byte(k))
			h.Write([]byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
			ak := base64.StdEncoding.EncodeToString(h.Sum(nil))
			if string(b[len("Sec-WebSocket-Accept: "):]) != ak {
				c.Close()
				return nil, errors.New(string(b))
			}
		}
	}
	return c, nil
}

type TLSFragmentConn struct {
	net.Conn

View on GitHub (pinned to 5cd13ef3b1)

Solutions

  1. Read the error message (it contains the full HTTP response) to identify the returned status code and fix accordingly
  2. Verify the WebSocket path, host and port are correct
  3. Ensure any required auth handshake with the server completed before dialing
  4. Bypass or correctly configure reverse proxies/CDNs to pass the Upgrade and Connection headers
  5. Confirm the server actually implements the WebSocket protocol on that endpoint

Example fix

// before: dialing plain server path with a WebSocket dialer
c, err := WebSocketDial("wss://host/plain-endpoint")

// after: use the endpoint that speaks WebSocket
c, err := WebSocketDial("wss://host/ws")
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check the endpoint accepts upgrades:
resp, err := http.Get(endpoint)
if err == nil && resp.StatusCode != http.StatusSwitchingProtocols { /* endpoint wrong */ }

Try / catch

c, err := WebSocketDial(url)
if err != nil {
	if strings.Contains(err.Error(), "401") || strings.Contains(err.Error(), "403") {
		// refresh auth, then retry
	} else if strings.Contains(err.Error(), "404") {
		// fix endpoint path
	}
	return err
}

Prevention

When it happens

Trigger: Calling WebSocketDial (directly or via CreateExchanger/TCPHandle/UDPHandle) when the WebSocket server replies with a non-101 HTTP/1.1 status - e.g. 404 on a wrong path, 401/403 auth rejected, 426 upgrade required, or 502 from an intermediary.

Common situations: Wrong WebSocket endpoint path or port; missing/expired auth accepted by an auth gateway in front of the socket; server not actually supporting WebSocket; a reverse proxy (nginx/CDN) blocking the Upgrade; target behind a captive portal returning an HTML error page.

Related errors


AI-assisted analysis of txthinking/brook@5cd13ef3b1 (2026-09-06). Data as JSON: /api/errors/f018e6c3d733cd33. Report an issue: GitHub.