{"record":{"id":"118ed0e3c1d6bcda","repo":"SigNoz/signoz","slug":"couldn-t-upgrade-connection-w","errorCode":null,"errorMessage":"couldn't upgrade connection: %w","messagePattern":"couldn't upgrade connection: %w","errorType":"http","errorClass":"model.ApiError","httpStatus":500,"severity":"error","filePath":"pkg/query-service/app/http_handler.go","lineNumber":3778,"sourceCode":"}\n\nfunc (aH *APIHandler) GetQueryProgressUpdates(w http.ResponseWriter, r *http.Request) {\n\t// Upgrade connection to websocket, sending back the requested protocol\n\t// value for sec-websocket-protocol\n\t//\n\t// Since js websocket API doesn't allow setting headers, this header is often\n\t// used for passing auth tokens. As per websocket spec the connection will only\n\t// succeed if the requested `Sec-Websocket-Protocol` is sent back as a header\n\t// in the upgrade response (signifying that the protocol is supported by the server).\n\tupgradeResponseHeaders := http.Header{}\n\trequestedProtocol := r.Header.Get(\"Sec-WebSocket-Protocol\")\n\tif len(requestedProtocol) > 0 {\n\t\tupgradeResponseHeaders.Add(\"Sec-WebSocket-Protocol\", requestedProtocol)\n\t}\n\n\tc, err := aH.Upgrader.Upgrade(w, r, upgradeResponseHeaders)\n\tif err != nil {\n\t\tRespondError(w, model.InternalError(fmt.Errorf(\n\t\t\t\"couldn't upgrade connection: %w\", err,\n\t\t)), nil)\n\t\treturn\n\t}\n\tdefer c.Close()\n\n\t// Websocket upgrade complete. Subscribe to query progress and send updates to client\n\t//\n\t// Note: we handle any subscription problems (queryId query param missing or query already complete etc)\n\t// after the websocket connection upgrade by closing the channel.\n\t// The other option would be to handle the errors before websocket upgrade by sending an\n\t// error response instead of the upgrade response, but that leads to a generic websocket\n\t// connection failure on the client.\n\n\tqueryId := r.URL.Query().Get(\"q\")\n\n\tprogressCh, unsubscribe, apiErr := aH.reader.SubscribeToQueryProgress(queryId)\n\tif apiErr != nil {","sourceCodeStart":3760,"sourceCodeEnd":3796,"githubUrl":"https://github.com/SigNoz/signoz/blob/5069bf80b08f1f00d7e014eccc09902f9871004f/pkg/query-service/app/http_handler.go#L3760-L3796","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before (default same-origin CheckOrigin rejects cross-origin dashboards)\nc, err := aH.Uppgrader.Upgrade(w, r, upgradeResponseHeaders)\n\n// after\nupgrader := websocket.Upgrader{\n    CheckOrigin: func(r *http.Request) bool {\n        origin := r.Header.Get(\"Origin\")\n        return origin == \"https://app.mycompany.com\" // or parse against an allowlist\n    },\n}\nc, err := aH.upgrader.Upgrade(w, r, upgradeResponseHeaders)","handlingStrategy":"fallback","validationCode":"// Before opening, verify the endpoint is a WS endpoint and proxy passes headers\nconst isWsCapable = (url) => url.startsWith('ws://') || url.startsWith('wss://');\nif (!isWsCapable(endpoint)) throw new Error('not a websocket url');","typeGuard":null,"tryCatchPattern":"try {\n  const ws = new WebSocket(url, protocols);\n  ws.onerror = () => { /* fall back to HTTP polling */ startPollingFallback(); };\n} catch (e) {\n  startPollingFallback();\n}","preventionTips":["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"],"tags":["websocket","connection-upgrade","gorilla-websocket","reverse-proxy","query-service"],"backgroundTag":"websocket-upgrade-failed","analyzedSha":"5069bf80b08f1f00d7e014eccc09902f9871004f","analyzedAt":"2026-08-28T06:22:12.824Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}