SigNoz/signoz · error · model.ApiError
couldn't upgrade connection: %w
Error message
couldn't upgrade connection: %w
What it means
This error is returned when the SigNoz query-service fails to promote an incoming HTTP request into a WebSocket connection via gorilla/websocket's Upgrader.Upgrade. The upgrade requires the request to be a valid GET with an Upgrade: websocket header, a matching Sec-WebSocket-Key, and any configured Origin/CORS checks to pass. Any deviation causes Upgrade to return an error, which the handler wraps and returns as a 500 InternalError.
Source
Thrown at pkg/query-service/app/http_handler.go:3778
}
func (aH *APIHandler) GetQueryProgressUpdates(w http.ResponseWriter, r *http.Request) {
// Upgrade connection to websocket, sending back the requested protocol
// value for sec-websocket-protocol
//
// Since js websocket API doesn't allow setting headers, this header is often
// used for passing auth tokens. As per websocket spec the connection will only
// succeed if the requested `Sec-Websocket-Protocol` is sent back as a header
// in the upgrade response (signifying that the protocol is supported by the server).
upgradeResponseHeaders := http.Header{}
requestedProtocol := r.Header.Get("Sec-WebSocket-Protocol")
if len(requestedProtocol) > 0 {
upgradeResponseHeaders.Add("Sec-WebSocket-Protocol", requestedProtocol)
}
c, err := aH.Upgrader.Upgrade(w, r, upgradeResponseHeaders)
if err != nil {
RespondError(w, model.InternalError(fmt.Errorf(
"couldn't upgrade connection: %w", err,
)), nil)
return
}
defer c.Close()
// Websocket upgrade complete. Subscribe to query progress and send updates to client
//
// Note: we handle any subscription problems (queryId query param missing or query already complete etc)
// after the websocket connection upgrade by closing the channel.
// The other option would be to handle the errors before websocket upgrade by sending an
// error response instead of the upgrade response, but that leads to a generic websocket
// connection failure on the client.
queryId := r.URL.Query().Get("q")
progressCh, unsubscribe, apiErr := aH.reader.SubscribeToQueryProgress(queryId)
if apiErr != nil {View on GitHub (pinned to 5069bf80b0)
Solutions
- Verify the client is issuing a proper WebSocket handshake (wss:// URL, browser WebSocket API, or a real WS client) against the correct endpoint
- Fix reverse-proxy config to pass through upgrade headers (nginx: proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; and disable buffering/timeouts for the WS route)
- If the frontend is served from a different origin, set aH.Upgrader.CheckOrigin to allow the dashboard origin rather than relying on the default same-origin check
- If it's a proxy idle-timeout disconnect, increase read/write deadlines on both proxy and server
Example fix
// before (default same-origin CheckOrigin rejects cross-origin dashboards)
c, err := aH.Uppgrader.Upgrade(w, r, upgradeResponseHeaders)
// after
upgrader := websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
origin := r.Header.Get("Origin")
return origin == "https://app.mycompany.com" // or parse against an allowlist
},
}
c, err := aH.upgrader.Upgrade(w, r, upgradeResponseHeaders) Defensive patterns
Strategy: fallback
Validate before calling
// Before opening, verify the endpoint is a WS endpoint and proxy passes headers
const isWsCapable = (url) => url.startsWith('ws://') || url.startsWith('wss://');
if (!isWsCapable(endpoint)) throw new Error('not a websocket url'); Try / catch
try {
const ws = new WebSocket(url, protocols);
ws.onerror = () => { /* fall back to HTTP polling */ startPollingFallback(); };
} catch (e) {
startPollingFallback();
} Prevention
- Configure reverse proxy to forward Upgrade/Connection headers for WS routes
- Set explicit long read/write deadlines instead of relying on defaults
- Implement HTTP polling fallback when the WS handshake fails
- Test WS connectivity in the target deployment environment, not just locally
When it happens
Trigger: GET /api/v1/ws/* (logs live-tail, widgets websocket) with a non-GET method, missing/incorrect Sec-WebSocket-* headers, a client that is not actually speaking WebSocket, or an Origin header rejected by the Upgrader's CheckOrigin policy (e.g., dashboard served from a different domain/port behind a misconfigured reverse proxy).
Common situations: Reverse proxies (nginx/traefik) that strip Upgrade/Connection headers or time out long-lived sockets; frontend served cross-origin while aH.Upgrader uses the default same-origin CheckOrigin; HTTP/1.0 clients or health-checkers probing the WS endpoint with plain GET; proxies buffering or cutting idle connections so the handshake never completes.
Related errors
- no operations table supplied
- no index table supplied
- start time is required for search queries
- removing a selected field is not allowed, please reach out t
- ErrorNotFound
AI-assisted analysis of SigNoz/signoz@5069bf80b0 (2026-08-28).
Data as JSON: /api/errors/118ed0e3c1d6bcda.
Report an issue: GitHub.