wavetermdev/waveterm · error

err.Error()

Error message

err.Error()

What it means

HandleWs (pkg/web/ws.go:70) wraps HandleWsInternal; if the WebSocket upgrade or setup fails, the resulting error is written via http.Error with status 500. Any failure to establish the WS connection (bad handshake, internal setup error) surfaces as this plain-text 500.

Source

Thrown at pkg/web/ws.go:70

	server.SetKeepAlivesEnabled(false)
	log.Printf("[websocket] running websocket server on %s\n", listener.Addr())
	err := server.Serve(listener)
	if err != nil {
		log.Printf("[websocket] error trying to run websocket server: %v\n", err)
	}
}

var WebSocketUpgrader = websocket.Upgrader{
	ReadBufferSize:   4 * 1024,
	WriteBufferSize:  32 * 1024,
	HandshakeTimeout: 1 * time.Second,
	CheckOrigin:      func(r *http.Request) bool { return true },
}

func HandleWs(w http.ResponseWriter, r *http.Request) {
	err := HandleWsInternal(w, r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func getMessageType(jmsg map[string]any) string {
	if str, ok := jmsg["type"].(string); ok {
		return str
	}
	return ""
}

func getStringFromMap(jmsg map[string]any, key string) string {
	if str, ok := jmsg[key].(string); ok {
		return str
	}
	return ""
}

func processWSCommand(jmsg map[string]any, outputCh chan any, rpcInputCh chan baseds.RpcInputChType) {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Read err.Error() in the 500 response body to see the exact handshake/setup failure.
  2. Configure the reverse proxy to pass Upgrade and Connection headers for the /ws route.
  3. Ensure the client uses wss:// when the server is behind TLS and includes any required query parameters (e.g. idempotency token).
  4. Confirm the route is registered (HandleWs wired into the mux) and the request method is GET.

Example fix

// nginx before
location /ws { proxy_pass http://backend; }

// after
location /ws {
  proxy_pass http://backend;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the endpoint is reachable and supports upgrade before opening WS
const probe = await fetch(wsBaseUrl.replace("ws", "http"), {method: "GET"});
if (probe.status >= 500) throw new Error("ws endpoint not healthy");

Try / catch

try {
  const sock = new WebSocket(wsUrl);
  sock.onerror = (e) => console.error("ws handshake failed", e);
} catch (err) {
  console.error("HandleWs returned 500:", err);
  // fall back to polling or retry with corrected headers
}

Prevention

When it happens

Trigger: HandleWsInternal returns an error — e.g. the websocket Upgrader fails the HTTP->WS handshake because required Upgrade/Connection headers are missing or stripped, or internal initialization inside HandleWsInternal fails.

Common situations: Reverse proxies not forwarding the Upgrade/Connection headers (nginx without proxy_set_header Upgrade $connection_upgrade), clients connecting over plain http where only https is served, an IdempotencyToken/query param missing in the WS URL.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/d4aa2ba72d090cdd. Report an issue: GitHub.