Skip to content

Commit 8de202b

Browse files
author
Eric Wheeler
committed
fix: prevent UI freeze when terminal stream is unavailable
Add timeout and error handling to terminal stream initialization to prevent UI from freezing when a stream is unavailable or never starts. This ensures that if the VSCE shell integration stream does not start within 3 seconds: - The streamAvailable promise is rejected with a clear error - Event listeners are cleaned up to prevent memory leaks - Terminal state is properly reset - Execution continues rather than hanging indefinitely This fixes a potential deadlock where the UI could freeze waiting for a stream that never becomes available. Signed-off-by: Eric Wheeler <[email protected]>
1 parent f2891d7 commit 8de202b

File tree

1 file changed

+39
-2
lines changed

1 file changed

+39
-2
lines changed

src/integrations/terminal/TerminalProcess.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,24 @@ export class TerminalProcess extends EventEmitter<TerminalProcessEvents> {
246246

247247
if (terminal.shellIntegration && terminal.shellIntegration.executeCommand) {
248248
// Create a promise that resolves when the stream becomes available
249-
const streamAvailable = new Promise<AsyncIterable<string>>((resolve) => {
249+
const streamAvailable = new Promise<AsyncIterable<string>>((resolve, reject) => {
250+
const timeoutId = setTimeout(() => {
251+
// Remove event listener to prevent memory leaks
252+
this.removeAllListeners("stream_available")
253+
254+
// Emit no_shell_integration event with descriptive message
255+
this.emit(
256+
"no_shell_integration",
257+
"VSCE shell integration stream did not start within 3 seconds. Terminal problem?",
258+
)
259+
260+
// Reject with descriptive error
261+
reject(new Error("VSCE shell integration stream did not start within 3 seconds."))
262+
}, 3000)
263+
264+
// Clean up timeout if stream becomes available
250265
this.once("stream_available", (stream: AsyncIterable<string>) => {
266+
clearTimeout(timeoutId)
251267
resolve(stream)
252268
})
253269
})
@@ -264,7 +280,28 @@ export class TerminalProcess extends EventEmitter<TerminalProcessEvents> {
264280
this.isHot = true
265281

266282
// Wait for stream to be available
267-
const stream = await streamAvailable
283+
let stream: AsyncIterable<string>
284+
try {
285+
stream = await streamAvailable
286+
} catch (error) {
287+
// Stream timeout or other error occurred
288+
console.error("[Terminal Process] Stream error:", error.message)
289+
290+
// Emit completed event with error message
291+
this.emit(
292+
"completed",
293+
"<VSCE shell integration stream did not start: terminal output and command execution status is unknown>",
294+
)
295+
296+
// Ensure terminal is marked as not busy
297+
if (this.terminalInfo) {
298+
this.terminalInfo.busy = false
299+
}
300+
301+
// Emit continue event to allow execution to proceed
302+
this.emit("continue")
303+
return
304+
}
268305

269306
let preOutput = ""
270307
let commandOutputStarted = false

0 commit comments

Comments
 (0)