fix: guard _resize against disposed _resizeDebouncer (fixes #317787)#317791
Merged
vs-code-engineering[bot] merged 1 commit intoMay 21, 2026
Merged
Conversation
When a terminal is being disposed, _resizeDebouncer is set to undefined before the dispose sequence completes. If a configuration change event fires during disposal (e.g. from terminalSuggestConfiguration registration), setVisible -> _resize is called and crashes trying to call .resize() on undefined. Add _resizeDebouncer to the early-return check at the top of _resize and replace the non-null assertion with optional chaining as a safety net. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
vijayupadya
approved these changes
May 21, 2026
vritant24
approved these changes
May 21, 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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
🔧 Error Fix
Summary
Error:
Cannot read properties of undefined (reading 'resize')interminalInstance._resizeRoot cause: Commit
dff2ee20("fix: dispose resize debouncer before process manager to prevent race", part of #315284) moved_resizeDebouncerdisposal earlier in the terminal teardown sequence — before_processManager.dispose(). This prevents resize callbacks from reaching a nulledptyProcessReady, but introduces a new race: configuration change events (e.g. fromterminalSuggestConfigurationregistration) can fire during the dispose sequence and triggersetVisible→_resize, which crashes because_resizeDebounceris alreadyundefined.Impact: 195 affected users across 4 coalesced buckets on all platforms.
Fixes #317787
Recommended reviewer:
@TyriarCulprit Commit
dff2ee20@vs-code-engineering[bot] /@bryanchen-d(co-author)Code Flow
sequenceDiagram participant User participant TI as TerminalInstance participant RD as ResizeDebouncer participant CS as ConfigService participant TSC as TerminalSuggestConfig User->>TI: dispose() TI->>RD: dispose() Note over TI: _resizeDebouncer = undefined TI->>CS: (dispose continues...) TSC->>CS: updateConfigurations() CS->>TI: onDidChangeConfiguration fires TI->>TI: setVisible(_isVisible) TI->>TI: _resize() Note over TI: this._resizeDebouncer!.resize() 💥 undefinedAffected Files
src/vs/workbench/contrib/terminal/browser/terminalInstance.ts_resizeuses_resizeDebouncer!after it's been disposedsrc/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.tsRepro Steps
setVisible→_resizeafter_resizeDebounceris already disposed_resizeDebouncer!.resize()How the Fix Works
Chosen approach (
src/vs/workbench/contrib/terminal/browser/terminalInstance.ts):Added
!this._resizeDebouncerto the existing early-return guard at the top of_resize(). When_resizeDebouncerisundefined(meaning the instance is mid-disposal), the method returns immediately before any resize logic executes. This is a guard clause upstream of the crash site — it prevents the error from being produced rather than catching it after the fact. Also replaced the!non-null assertion on the.resize()call with optional chaining (?.) as a safety net against any other path that could reach this line post-disposal.This follows the principle of fixing at the data producer (the disposal sequence that nulls
_resizeDebouncer) by guarding callers that can still fire during that window, rather than wrapping the crash in try/catch.Recommended Owner
@Tyriar