From 2940b50811443d6dabeeec9b645e4930e8016ea6 Mon Sep 17 00:00:00 2001 From: Mayur Vaid <34806097+MayV@users.noreply.github.com> Date: Wed, 22 Oct 2025 09:57:49 +0530 Subject: [PATCH] fix: Ignore correct errors thrown when resizing or scrolling an exited pty (#11440) --- .../services/shellExecutionService.test.ts | 48 +++++++++++++++++++ .../src/services/shellExecutionService.ts | 6 ++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/packages/core/src/services/shellExecutionService.test.ts b/packages/core/src/services/shellExecutionService.test.ts index 45aa9fce97..372077139e 100644 --- a/packages/core/src/services/shellExecutionService.test.ts +++ b/packages/core/src/services/shellExecutionService.test.ts @@ -291,6 +291,54 @@ describe('ShellExecutionService', () => { expect(mockHeadlessTerminal.resize).toHaveBeenCalledWith(100, 40); }); + it('should not resize the pty if it is not active', async () => { + const isPtyActiveSpy = vi + .spyOn(ShellExecutionService, 'isPtyActive') + .mockReturnValue(false); + + await simulateExecution('ls -l', (pty) => { + ShellExecutionService.resizePty(pty.pid!, 100, 40); + pty.onExit.mock.calls[0][0]({ exitCode: 0, signal: null }); + }); + + expect(mockPtyProcess.resize).not.toHaveBeenCalled(); + expect(mockHeadlessTerminal.resize).not.toHaveBeenCalled(); + isPtyActiveSpy.mockRestore(); + }); + + it('should ignore errors when resizing an exited pty', async () => { + const resizeError = new Error( + 'Cannot resize a pty that has already exited', + ); + mockPtyProcess.resize.mockImplementation(() => { + throw resizeError; + }); + + // We don't expect this test to throw an error + await expect( + simulateExecution('ls -l', (pty) => { + ShellExecutionService.resizePty(pty.pid!, 100, 40); + pty.onExit.mock.calls[0][0]({ exitCode: 0, signal: null }); + }), + ).resolves.not.toThrow(); + + expect(mockPtyProcess.resize).toHaveBeenCalledWith(100, 40); + }); + + it('should re-throw other errors during resize', async () => { + const otherError = new Error('Some other error'); + mockPtyProcess.resize.mockImplementation(() => { + throw otherError; + }); + + await expect( + simulateExecution('ls -l', (pty) => { + ShellExecutionService.resizePty(pty.pid!, 100, 40); + pty.onExit.mock.calls[0][0]({ exitCode: 0, signal: null }); + }), + ).rejects.toThrow('Some other error'); + }); + it('should scroll the headless terminal', async () => { await simulateExecution('ls -l', (pty) => { pty.onData.mock.calls[0][0]('file1.txt\n'); diff --git a/packages/core/src/services/shellExecutionService.ts b/packages/core/src/services/shellExecutionService.ts index d5dc17bb14..eeec882ca8 100644 --- a/packages/core/src/services/shellExecutionService.ts +++ b/packages/core/src/services/shellExecutionService.ts @@ -750,7 +750,11 @@ export class ShellExecutionService { } catch (e) { // Ignore errors if the pty has already exited, which can happen // due to a race condition between the exit event and this call. - if (e instanceof Error && 'code' in e && e.code === 'ESRCH') { + if ( + e instanceof Error && + (('code' in e && e.code === 'ESRCH') || + e.message === 'Cannot resize a pty that has already exited') + ) { // ignore } else { throw e;