charmbracelet/crush · error
failed to subscribe to events
Error message
failed to subscribe to events: %w
What it means
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.
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.
Example fix
// before
events, err := client.SubscribeEvents(ctx, id)
if err != nil { return err }
// after: retry stream establishment
var events <-chan any
err := client.SubscribeEventsErr() // wrapper
for i := 0; i < 3; i++ {
events, err = client.SubscribeEvents(ctx, id)
if err == nil { break }
if errors.Is(err, context.Canceled) { return err }
time.Sleep(500 * time.Millisecond << i)
} Defensive patterns
Strategy: retry
Validate before calling
if ctx.Err() != nil { return fmt.Errorf("cannot subscribe: context already done") }
if err := serverReachable(serverURL); err != nil { return err } Try / catch
var events <-chan any
var err error
for attempt := 0; attempt < 5; attempt++ {
events, err = client.SubscribeEvents(ctx, id)
if err == nil { break }
if errors.Is(err, context.Canceled) { return err }
select {
case <-time.After(time.Second << attempt):
case <-ctx.Done():
return ctx.Err()
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- Crush was unable to fetch an updated list of providers from
- Crush was unable to fetch updated information from Hyper
- empty providers list from catwalk
- failed to answer question batch
- failed to authenticate MCP
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/a2b3494caa3e25c1.
Report an issue: GitHub.
Appendix: source
Thrown at internal/client/proto.go:124
defer rsp.Body.Close()
if err := checkStatus(rsp); err != nil {
return fmt.Errorf("failed to set current session: %w", err)
}
return nil
}
// SubscribeEvents subscribes to server-sent events for a workspace.
func (c *Client) SubscribeEvents(ctx context.Context, id string) (<-chan any, error) {
events := make(chan any, 100)
q := url.Values{"client_id": []string{c.clientID}}
//nolint:bodyclose
rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/events", id), q, http.Header{
"Accept": []string{"text/event-stream"},
"Cache-Control": []string{"no-cache"},
"Connection": []string{"keep-alive"},
})
if err != nil {
return nil, fmt.Errorf("failed to subscribe to events: %w", err)
}
if err := checkStatus(rsp); err != nil {
rsp.Body.Close()
return nil, fmt.Errorf("failed to subscribe to events: %w", err)
}
go func() {
defer rsp.Body.Close()
defer close(events)
scr := bufio.NewReader(rsp.Body)
for {
line, err := scr.ReadBytes('\n')
if errors.Is(err, io.EOF) {
break
}
if err != nil {View on GitHub (pinned to 7944b8e522)