Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 20, 2026

DetachedTerminalSnapshotMirror can be disposed while async operations (attach(), render()) are in flight. Subsequent access to terminal.xterm.buffer triggers xterm's internal lazy registration, causing "Trying to add a disposable to a DisposableStore that has already been disposed of" errors.

Changes

  • Constructor: Dispose terminal immediately if store is already disposed when promise resolves
  • attach(): Early return after awaiting terminal if disposed
  • render(): Early return after _getTerminal() and after terminal.xterm.write() if disposed
// Before: No disposal check after async
const terminal = await this._getTerminal();
this._computeMaxColumnWidth(terminal); // Throws if disposed

// After: Guard async continuations
const terminal = await this._getTerminal();
if (this._store.isDisposed) {
    return undefined;
}
this._computeMaxColumnWidth(terminal);

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • redirector.gvt1.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --crashpad-handler-pid=6781 --enable-crash-reporter=7406e0f2-c70e-45b3-a85a-795a433530b7,no_channel --user-data-dir=/tmp/vscode-tests-1768928301321 --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,12506016518474805750,15792222962786304686,262144 --enable-features=PdfUseShowSaveFilePicker --disable-features=LocalNetworkAccessChecks,ScreenAIOCREnabled,SpareRendererForSitePerProcess,TraceSiteInstanceGetProcessCreation --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
    • Triggering command: .build/electron/code-oss .build/electron/code-oss test/unit/electron/index.js --crash-reporter-directory=/home/REDACTED/work/vscode/vscode/.build/crashes --runGlob **/chatTerminalCommandMirror.test.js (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --crashpad-handler-pid=8480 --enable-crash-reporter=7406e0f2-c70e-45b3-a85a-795a433530b7,no_channel --user-data-dir=/tmp/vscode-tests-1768928654352 --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,14289129642060378853,1945214153318072413,262144 --enable-features=PdfUseShowSaveFilePicker --disable-features=LocalNetworkAccessChecks,ScreenAIOCREnabled,SpareRendererForSitePerProcess,TraceSiteInstanceGetProcessCreation --variations-seed-version --trace-process-track-uuid=3190708989122997041 d tsgo --project .ldd ommandMirror.tes/home/REDACTED/.cache/ms-playwright/webkit-2215/minibrowser-wpe/sys/lib/libsoup-3.0.so.0.7.4 odules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin/ldd ldd (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Trying to add a disposable to a DisposableStore that has already been disposed of</issue_title>
<issue_description>On latest main and still running into these

xtermTerminal.ts:110 Error: Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!
    at g.add (xterm.js:1:367549)
    at u._register (xterm.js:1:368195)
    at get buffer (xterm.js:1:500912)
    at get buffer (xtermTerminal.ts:110:33)
    at DetachedTerminalSnapshotMirror._computeMaxColumnWidth (chatTerminalCommandMirror.ts:638:53)
    at DetachedTerminalSnapshotMirror.render (chatTerminalCommandMirror.ts:632:44)
    at async ChatTerminalToolOutputSection._renderSnapshotOutput (chatTerminalToolProgressPart.ts:1050:18)
    at async ChatTerminalToolOutputSection._updateTerminalContent (chatTerminalToolProgressPart.ts:997:4)
    at async ChatTerminalToolOutputSection.toggle (chatTerminalToolProgressPart.ts:878:3)
    at async ChatTerminalToolProgressPart._toggleOutput (chatTerminalToolProgressPart.ts:666:21)
```</issue_description>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits January 20, 2026 17:19
Add disposal checks in async methods to prevent accessing
disposed terminal after component disposal:
- Check _store.isDisposed after async terminal retrieval
- Check after terminal.xterm.write() completes
- Handle terminal disposal gracefully in constructor promise

Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com>
Co-authored-by: meganrogge <29464607+meganrogge@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix error when adding disposable to disposed DisposableStore Fix disposed store error in DetachedTerminalSnapshotMirror Jan 20, 2026
Copilot AI requested a review from meganrogge January 20, 2026 17:24
@meganrogge meganrogge marked this pull request as ready for review January 21, 2026 21:10
Copilot AI review requested due to automatic review settings January 21, 2026 21:10
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a race condition where DetachedTerminalSnapshotMirror can be disposed while async operations are in flight, causing "Trying to add a disposable to a DisposableStore that has already been disposed of" errors when accessing terminal.xterm.buffer.

Changes:

  • Added disposal checks after async operations in constructor, attach(), and render() methods
  • Added test-output.json to .gitignore to exclude test artifacts

Reviewed changes

Copilot reviewed 1 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/vs/workbench/contrib/terminal/browser/chatTerminalCommandMirror.ts Added disposal checks in DetachedTerminalSnapshotMirror after awaiting terminal to prevent accessing disposed terminal buffers
.gitignore Excluded test-output.json test artifact from version control

Comment on lines +603 to +605
if (this._store.isDisposed) {
return;
}
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The disposal check guards added to prevent the "Trying to add a disposable to a DisposableStore that has already been disposed of" error are not covered by tests. Consider adding a test case that simulates the disposal race condition by disposing the DetachedTerminalSnapshotMirror while async operations (attach() or render()) are in flight, to ensure the disposal checks work as intended and prevent the error.

Copilot uses AI. Check for mistakes.
@meganrogge meganrogge merged commit 2b514ef into main Jan 21, 2026
27 of 28 checks passed
@meganrogge meganrogge deleted the copilot/fix-disposable-store-error branch January 21, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trying to add a disposable to a DisposableStore that has already been disposed of

3 participants