{"record":{"id":"079676706d2c344d","repo":"charmbracelet/crush","slug":"the-event-stream-closed-reconnecting","errorCode":null,"errorMessage":"the event stream closed; reconnecting","messagePattern":"the event stream closed; reconnecting","errorType":"exception","errorClass":"ErrStreamClosed","httpStatus":null,"severity":"warning","filePath":"internal/workspace/workspace.go","lineNumber":48,"sourceCode":"// uninitialized agent apart from a lost server connection.\nvar (\n\t// ErrAgentNotInitialized means the workspace exists but its coder\n\t// agent has not been configured/initialized (e.g. no model set).\n\tErrAgentNotInitialized = errors.New(\"coder agent is not initialized\")\n\t// ErrServerUnreachable means the client could not reach the server\n\t// to determine the agent's status (server down, or the workspace was\n\t// torn down out from under the client).\n\tErrServerUnreachable = errors.New(\"lost connection to the crush server\")\n\t// ErrWorkspaceGone means the server is reachable but no longer knows\n\t// this client's workspace: it was torn down, or the server was\n\t// replaced underneath the client. The subscription loop re-registers\n\t// the workspace in the background when it sees this.\n\tErrWorkspaceGone = errors.New(\"the server reset this workspace; reconnecting\")\n\t// ErrStreamClosed means an established event stream ended.\n\t// Resubscribing usually succeeds immediately, but events published in\n\t// the meantime are lost for good, so the client treats it as a\n\t// degraded link that requires a resync.\n\tErrStreamClosed = errors.New(\"the event stream closed; reconnecting\")\n)\n\n// ConnectionState describes the health of the client-server link as\n// reported by the [ClientWorkspace] subscription loop.\ntype ConnectionState int\n\nconst (\n\t// ConnectionDegraded means the event stream is down (or the workspace\n\t// was lost server-side) and the client is retrying or re-registering\n\t// in the background.\n\tConnectionDegraded ConnectionState = iota\n\t// ConnectionRecovered means the event stream was re-established,\n\t// possibly against a re-created workspace.\n\tConnectionRecovered\n)\n\n// ConnectionEvent is delivered to the TUI as a tea.Msg on degraded and\n// recovered transitions of the client-server link. Local (in-process)","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/workspace/workspace.go#L30-L66","documentation":"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.","triggerScenarios":"runSubscription's established event stream terminates (server-side stream close, transient network interruption) after a successful subscribe.","commonSituations":"Idle connections reaped by proxies or load balancers; server gracefully closing streams during restart; long-running sessions where the SSE/stream connection times out.","solutions":["Resubscribe immediately (the loop usually reconnects at once)","Trigger a state resync after reconnection to recover events lost during the gap","Enable TCP keepalives or shorter proxy idle timeouts to reduce silent stream drops"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isStreamClosed(err error) bool { return errors.Is(err, workspace.ErrStreamClosed) }","tryCatchPattern":"if err != nil {\n    if errors.Is(err, workspace.ErrStreamClosed) {\n        resubscribe(ctx)\n        resyncState(ctx) // events published during the gap are lost\n        return nil\n    }\n    return err\n}","preventionTips":["Always resync local state after ErrStreamClosed; gap events are permanently lost","Configure keepalives/proxy idle timeouts above the stream heartbeat","Treat this as degraded-link telemetry to detect flaky networks"],"tags":["streaming","pubsub","reconnect"],"backgroundTag":"event-stream-closed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}