Fix DisposableStore reference leak in terminal execute strategies#313369
Merged
Conversation
Each execute() call registered a per-call DisposableStore via this._register(store), but store.dispose() in the finally block does not remove the reference from the parent _toDispose Set. Stale, already-disposed stores accumulated across invocations on long-lived strategies (28 observed in the wild). Track in-flight stores in a dedicated _executionStores DisposableStore and delete(store) on completion. Preserves the mid-flight disposal cleanup from #310157 while clearing the reference on the happy path. Fixes #313368
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a lifecycle leak in the terminal chat agent “run in terminal” execute strategies by ensuring per-invocation DisposableStores don’t remain referenced by long-lived strategy instances after execute() completes.
Changes:
- Introduce a dedicated
_executionStoresDisposableStoreon each execute strategy to track in-flight per-call stores. - Replace
this._register(store)with_executionStores.add(store)so stores can be removed after execution. - Replace
store.dispose()with_executionStores.delete(store)to both dispose and remove references on completion.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/executeStrategy/richExecuteStrategy.ts | Track per-execution disposables via _executionStores and delete them in finally to avoid retained references. |
| src/vs/workbench/contrib/terminalContrib/chatAgentTools/browser/executeStrategy/basicExecuteStrategy.ts | Same disposal/reference cleanup pattern applied to the basic execute strategy. |
Copilot's findings
- Files reviewed: 2/2 changed files
- Comments generated: 2
roblourens
approved these changes
Apr 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #313368
Problem
RichExecuteStrategy.execute()andBasicExecuteStrategy.execute()each create a per-callDisposableStoreand register it on the strategy viathis._register(store)(introduced in #310157). Thefinally { store.dispose(); }runs the children's disposers but does not remove the entry from the parent's_toDisposeSet — onlyDisposableStore.delete(child)does. So every successfulexecute()leaves a stale (already-disposed)DisposableStorereferenced by the strategy. A colleague observed 28 such retained stores on a single long-lived strategy.Fix
Track in-flight stores in a dedicated
_executionStoresDisposableStore field anddelete(store)on completion. This preserves the mid-flight disposal cleanup behavior from #310157 (if the strategy is disposed whileexecute()is awaiting, the in-flight store is cleaned up) while clearing the reference on the happy path.