Memory leak: SDK event listeners never unsubscribed in App component #5387

Open
opened 2026-02-16 17:52:05 -05:00 by yindo · 0 comments
Owner

Originally created by @sauerdaniel on GitHub (Jan 13, 2026).

Originally assigned to: @rekram1-node on GitHub.

Problem

The TUI App component subscribes to 6 SDK events but never unsubscribes from them:

  • TuiEvent.CommandExecute
  • TuiEvent.ToastShow
  • TuiEvent.SessionSelect
  • SessionApi.Event.Deleted
  • SessionApi.Event.Error
  • Installation.Event.UpdateAvailable

The sdk.event.on() method returns an unsubscribe function that is being ignored.

Code Location

packages/opencode/src/cli/cmd/tui/app.tsx - lines 557-616

Impact

  • Event listeners accumulate over component lifecycle
  • Closures retain references preventing garbage collection
  • Memory growth in long-running sessions
  • Potential duplicate event handling

Relates to #5363

Expected Behavior

Event listeners should be stored and unsubscribed in onCleanup:

const unsubs: (() => void)[] = []

unsubs.push(sdk.event.on(TuiEvent.CommandExecute.type, (evt) => {
  command.trigger(evt.properties.command)
}))

// ... other listeners

onCleanup(() => {
  unsubs.forEach((unsub) => unsub())
})
Originally created by @sauerdaniel on GitHub (Jan 13, 2026). Originally assigned to: @rekram1-node on GitHub. ## Problem The TUI `App` component subscribes to 6 SDK events but never unsubscribes from them: - `TuiEvent.CommandExecute` - `TuiEvent.ToastShow` - `TuiEvent.SessionSelect` - `SessionApi.Event.Deleted` - `SessionApi.Event.Error` - `Installation.Event.UpdateAvailable` The `sdk.event.on()` method returns an unsubscribe function that is being ignored. ### Code Location `packages/opencode/src/cli/cmd/tui/app.tsx` - lines 557-616 ### Impact - Event listeners accumulate over component lifecycle - Closures retain references preventing garbage collection - Memory growth in long-running sessions - Potential duplicate event handling Relates to #5363 ## Expected Behavior Event listeners should be stored and unsubscribed in `onCleanup`: ```typescript const unsubs: (() => void)[] = [] unsubs.push(sdk.event.on(TuiEvent.CommandExecute.type, (evt) => { command.trigger(evt.properties.command) })) // ... other listeners onCleanup(() => { unsubs.forEach((unsub) => unsub()) }) ```
yindo added the perf label 2026-02-16 17:52:05 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#5387