fix: guard setDimensions against disposed ptyProcessReady (fixes #315282)#315284
fix: guard setDimensions against disposed ptyProcessReady (fixes #315282)#315284vs-code-engineering[bot] wants to merge 1 commit intomainfrom
Conversation
) When a terminal process manager is disposed, ptyProcessReady is set to undefined. If setDimensions is called after disposal (e.g., during a resize triggered by a configuration change event that fires during the dispose sequence), calling .then() on undefined throws a TypeError. Add a guard clause to return early when ptyProcessReady is undefined, preventing the crash without masking the error from telemetry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Fixes a terminal resize/dispose race in the workbench terminal process manager by preventing setDimensions from calling .then() on a disposed/cleared ptyProcessReady promise reference.
Changes:
- Add an early-return guard in
TerminalProcessManager.setDimensionswhenptyProcessReadyhas been cleared during disposal, avoiding aCannot read properties of undefined (reading 'then')crash.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts | Add guard in setDimensions to safely no-op when ptyProcessReady is unset during disposal. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 0
| return; | ||
| } | ||
|
|
||
| if (!this.ptyProcessReady) { |
There was a problem hiding this comment.
Is this new error from your recently merged PR?
Please, it would be good to have repro step and have you confirm that this resolves it.
This reads like startup state and silently resolving resize could hide real lifecycle bug.
Maybe there is something going on in debouncer with updatePtyDimension, and we should make sure that doesn't get invoked after disposable.
There was a problem hiding this comment.
Thanks for the review. To clarify the race condition:
- The
TerminalResizeDebounceralready guards against disposal (this._store.isDisposedcheck at line 36 ofterminalResizeDebouncer.ts), and_updatePtyDimensionsinterminalInstance.tsguards withthis.isDisposed(line 2059). - However, these guards check the terminal instance's disposal state, not the process manager's. The race occurs because the process manager's
ptyProcessReadyis nulled (line 582–584) during its own disposal handler, while the terminal instance hasn't fully disposed yet — so both the debouncer and_updatePtyDimensionsguards pass. - Fixing this in the debouncer wouldn't help because the debouncer belongs to the terminal instance, not the process manager — it can't know the process manager's internal state.
That said, the reviewer's concern about whether this masks a deeper lifecycle ordering issue is valid and warrants input from @bryanchen-d (the assignee and author of the culprit PR #314795). Deferring to humans on whether a broader fix to the disposal ordering is preferred over this defensive guard.
Generated by errors-fix-driver · ● 3.8M
🔧 Error Fix
Summary
Error:
Cannot read properties of undefined (reading 'then')interminalProcessManager.ts:608Root cause: When the terminal process manager is disposed,
ptyProcessReadyis set toundefined!(line 583). However,setDimensionscan still be called after disposal due to a race condition: a configuration change event fires during the terminal disposal sequence, triggeringsetVisible→_resize→resize→_resizeBothCallback→_updatePtyDimensions→setDimensions. TheisDisposedguard in_updatePtyDimensionspasses because the terminal instance isn't fully disposed yet, but the process manager'sptyProcessReadyhas already been nulled by its own disposal handler.Fixes #315282
Recommended reviewer:
@TyriarCulprit Commit
363a5254@bryanchen-dThis PR added guards to
terminalInstance.tsandterminalResizeDebouncer.tsfor resize/dispose races, but missed guarding the downstreamsetDimensionscall interminalProcessManager.tswhereptyProcessReadycan be undefined after disposal.Code Flow
graph TD A["Configuration change fires during disposal"] --> B["setVisible()"] B --> C["_resize()"] C --> D["resizeDebouncer.resize()"] D --> E["_resizeBothCallback()"] E --> F["_updatePtyDimensions()"] F --> G["processManager.setDimensions(cols, rows, false)"] G --> H["this.ptyProcessReady.then() — CRASH: ptyProcessReady is undefined"]Affected Files
src/vs/workbench/contrib/terminal/browser/terminalProcessManager.tsRepro Steps
setDimensionsis called on the already-disposed process managerptyProcessReadyisundefined, calling.then()on it throws TypeErrorHow the Fix Works
Chosen approach (
terminalProcessManager.ts:608): Added a guard clauseif (!this.ptyProcessReady) return Promise.resolve()before calling.then()onptyProcessReady. This follows the principle of adding a guard clause upstream of the crash site so the error path is never reached. The process manager is already disposed at this point, so skipping the resize is correct — there is no PTY to resize.Alternatives considered: Adding a try/catch around the
.then()call was rejected because it would hide the error from telemetry rather than preventing it at the source. Checkingthis._isDisposedwould also work but is less direct — the undefinedptyProcessReadyis the actual invariant being violated.Recommended Owner
@Tyriar— primary terminal area owner, responsible forterminalProcessManager.tserrors-fix-driver — cycle 1
Trigger: cron_changes_requested · Head:
6601c31c6b5abe060be207907bd3bdcc6f13c418(6601c31)terminalProcessManager.ts:608by@anthonykim1(CHANGES_REQUESTED)@bryanchen-d.Push: no — no code changes this cycle · Copilot rerequested: skipped (no push)
Ready gate: unresolved review thread with CHANGES_REQUESTED → not marking ready this cycle