{"record":{"id":"411f7bc4a8e26e73","repo":"henrygd/beszel","slug":"failed-to-send-request-w","errorCode":null,"errorMessage":"failed to send request: %w","messagePattern":"failed to send request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/hub/ws/request_manager.go","lineNumber":79,"sourceCode":"\t\tContext:    reqCtx,\n\t\tCancel:     cancel,\n\t\tCreatedAt:  time.Now(),\n\t}\n\n\trm.Lock()\n\trm.pendingReqs[reqID] = req\n\trm.Unlock()\n\n\thubReq := common.HubRequest[any]{\n\t\tId:     (*uint32)(&reqID),\n\t\tAction: action,\n\t\tData:   data,\n\t}\n\n\t// Send the request\n\tif err := rm.sendMessage(hubReq); err != nil {\n\t\trm.cancelRequest(reqID)\n\t\treturn nil, fmt.Errorf(\"failed to send request: %w\", err)\n\t}\n\n\t// Start cleanup watcher for timeout/cancellation\n\tgo rm.cleanupRequest(req)\n\n\treturn req, nil\n}\n\n// sendMessage encodes and sends a message over WebSocket\nfunc (rm *RequestManager) sendMessage(data any) error {\n\tif rm.conn == nil {\n\t\treturn gws.ErrConnClosed\n\t}\n\n\tbytes, err := cbor.Marshal(data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to marshal request: %w\", err)\n\t}","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/internal/hub/ws/request_manager.go#L61-L97","documentation":"SendRequest registers a pending request and then writes the CBOR-encoded HubRequest over the WebSocket via sendMessage. If the write fails, the pending request is cancelled and this wrapped error is returned, so the caller never gets a response channel result.","triggerScenarios":"sendMessage returns an error: connection is nil/closed (gws.ErrConnClosed), the underlying WebSocket write fails due to a dropped TCP connection, or the peer is unreachable mid-request.","commonSituations":"Agent disconnected or restarting when the hub sends a request; network interruption/firewall drop; hub trying to talk to an agent whose WS session already closed.","solutions":["Check rm.IsConnected/conn state before sending and reconnect if needed","Retry the request after the WebSocket connection is re-established","Inspect the wrapped cause (%w) — often gws.ErrConnClosed — to distinguish closed-connection from transient write errors"],"exampleFix":"// before\nresp, err := rm.SendRequest(ctx, action, data)\nif err != nil { return err }\n// after\nresp, err := rm.SendRequest(ctx, action, data)\nif err != nil {\n    if errors.Is(err, gws.ErrConnClosed) { reconnect(); resp, err = rm.SendRequest(ctx, action, data) }\n    if err != nil { return err }\n}","handlingStrategy":"retry","validationCode":"if !rm.IsConnected() {\n    return errors.New(\"websocket not connected; reconnect before sending\")\n}","typeGuard":null,"tryCatchPattern":"resp, err := rm.SendRequest(ctx, action, data)\nif err != nil {\n    if errors.Is(err, gws.ErrConnClosed) {\n        // reconnect and retry once\n    }\n    return fmt.Errorf(\"hub request failed: %w\", err)\n}","preventionTips":["Check connection state before sending","Use errors.Is to inspect the wrapped cause","Reconnect the WebSocket on ErrConnClosed before retrying","Set sensible request timeouts via ctx"],"tags":["websocket","network","send-failure"],"backgroundTag":"websocket-send-failed","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}