{"record":{"id":"a2b3494caa3e25c1","repo":"charmbracelet/crush","slug":"failed-to-subscribe-to-events-w","errorCode":null,"errorMessage":"failed to subscribe to events: %w","messagePattern":"failed to subscribe to events: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":124,"sourceCode":"\tdefer rsp.Body.Close()\n\tif err := checkStatus(rsp); err != nil {\n\t\treturn fmt.Errorf(\"failed to set current session: %w\", err)\n\t}\n\treturn nil\n}\n\n// SubscribeEvents subscribes to server-sent events for a workspace.\nfunc (c *Client) SubscribeEvents(ctx context.Context, id string) (<-chan any, error) {\n\tevents := make(chan any, 100)\n\tq := url.Values{\"client_id\": []string{c.clientID}}\n\t//nolint:bodyclose\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/events\", id), q, http.Header{\n\t\t\"Accept\":        []string{\"text/event-stream\"},\n\t\t\"Cache-Control\": []string{\"no-cache\"},\n\t\t\"Connection\":    []string{\"keep-alive\"},\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to subscribe to events: %w\", err)\n\t}\n\n\tif err := checkStatus(rsp); err != nil {\n\t\trsp.Body.Close()\n\t\treturn nil, fmt.Errorf(\"failed to subscribe to events: %w\", err)\n\t}\n\n\tgo func() {\n\t\tdefer rsp.Body.Close()\n\t\tdefer close(events)\n\n\t\tscr := bufio.NewReader(rsp.Body)\n\t\tfor {\n\t\t\tline, err := scr.ReadBytes('\\n')\n\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif err != nil {","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L106-L142","documentation":"SubscribeEvents wraps an error from c.get when opening the SSE stream GET /workspaces/{id}/events?client_id=... with event-stream headers. Raised before any status check, it means the transport itself failed: the stream could not be opened. The SSE channel is never created in this case and the wrapped cause (dial error, context canceled, TLS failure) is preserved.","triggerScenarios":"Calling client.SubscribeEvents(ctx, id) when the server is unreachable, the connection is refused/reset during handshake, or ctx is canceled before the request completes. Called by runNonInteractive and tests like TestSubscribeEventsContextCancelClosesEvents.","commonSituations":"runNonInteractive starting against a dead server; context canceled during startup (e.g. ctrl-c before the stream opens); wrong port/URL; proxy stripping long-lived connections.","solutions":["Confirm the server is up and /workspaces/{id}/events is reachable (curl with Accept: text/event-stream).","Check errors.Is(err, context.Canceled) to distinguish deliberate cancellation from real failures.","Retry SubscribeEvents with backoff; SSE reconnects are expected to be re-established.","Validate the server URL/port configuration."],"exampleFix":"// before\nevents, err := client.SubscribeEvents(ctx, id)\nif err != nil { return err }\n// after: retry stream establishment\nvar events <-chan any\nerr := client.SubscribeEventsErr() // wrapper\nfor i := 0; i < 3; i++ {\n    events, err = client.SubscribeEvents(ctx, id)\n    if err == nil { break }\n    if errors.Is(err, context.Canceled) { return err }\n    time.Sleep(500 * time.Millisecond << i)\n}","handlingStrategy":"retry","validationCode":"if ctx.Err() != nil { return fmt.Errorf(\"cannot subscribe: context already done\") }\nif err := serverReachable(serverURL); err != nil { return err }","typeGuard":null,"tryCatchPattern":"var events <-chan any\nvar err error\nfor attempt := 0; attempt < 5; attempt++ {\n    events, err = client.SubscribeEvents(ctx, id)\n    if err == nil { break }\n    if errors.Is(err, context.Canceled) { return err }\n    select {\n    case <-time.After(time.Second << attempt):\n    case <-ctx.Done():\n        return ctx.Err()\n    }\n}","preventionTips":["Start the server before opening SSE subscriptions.","Always implement reconnect with backoff for SSE.","Distinguish context cancellation from transport errors.","Test SSE handshake with curl -N and Accept: text/event-stream."],"tags":["network","sse","event-stream"],"backgroundTag":"connection-refused","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}