charmbracelet/crush · warning · ErrStreamClosed

the event stream closed; reconnecting

Error message

the event stream closed; reconnecting

What it means

ErrStreamClosed means an established event subscription stream ended. Resubscribing usually succeeds immediately, but any events published while the stream was down are lost for good, so the client treats it as a degraded link requiring a resync.

Source

Thrown at internal/workspace/workspace.go:48

// uninitialized agent apart from a lost server connection.
var (
	// ErrAgentNotInitialized means the workspace exists but its coder
	// agent has not been configured/initialized (e.g. no model set).
	ErrAgentNotInitialized = errors.New("coder agent is not initialized")
	// ErrServerUnreachable means the client could not reach the server
	// to determine the agent's status (server down, or the workspace was
	// torn down out from under the client).
	ErrServerUnreachable = errors.New("lost connection to the crush server")
	// ErrWorkspaceGone means the server is reachable but no longer knows
	// this client's workspace: it was torn down, or the server was
	// replaced underneath the client. The subscription loop re-registers
	// the workspace in the background when it sees this.
	ErrWorkspaceGone = errors.New("the server reset this workspace; reconnecting")
	// ErrStreamClosed means an established event stream ended.
	// Resubscribing usually succeeds immediately, but events published in
	// the meantime are lost for good, so the client treats it as a
	// degraded link that requires a resync.
	ErrStreamClosed = errors.New("the event stream closed; reconnecting")
)

// ConnectionState describes the health of the client-server link as
// reported by the [ClientWorkspace] subscription loop.
type ConnectionState int

const (
	// ConnectionDegraded means the event stream is down (or the workspace
	// was lost server-side) and the client is retrying or re-registering
	// in the background.
	ConnectionDegraded ConnectionState = iota
	// ConnectionRecovered means the event stream was re-established,
	// possibly against a re-created workspace.
	ConnectionRecovered
)

// ConnectionEvent is delivered to the TUI as a tea.Msg on degraded and
// recovered transitions of the client-server link. Local (in-process)

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Resubscribe immediately (the loop usually reconnects at once)
  2. Trigger a state resync after reconnection to recover events lost during the gap
  3. Enable TCP keepalives or shorter proxy idle timeouts to reduce silent stream drops
Defensive patterns

Strategy: retry

Type guard

func isStreamClosed(err error) bool { return errors.Is(err, workspace.ErrStreamClosed) }

Try / catch

if err != nil {
    if errors.Is(err, workspace.ErrStreamClosed) {
        resubscribe(ctx)
        resyncState(ctx) // events published during the gap are lost
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: runSubscription's established event stream terminates (server-side stream close, transient network interruption) after a successful subscribe.

Common situations: Idle connections reaped by proxies or load balancers; server gracefully closing streams during restart; long-running sessions where the SSE/stream connection times out.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/079676706d2c344d. Report an issue: GitHub.