@@ -38,6 +38,7 @@ import {
3838} from './sandboxManager.js' ;
3939import type { SandboxConfig } from '../config/config.js' ;
4040import { killProcessGroup } from '../utils/process-utils.js' ;
41+ import { isNodeError } from '../utils/errors.js' ;
4142import {
4243 ExecutionLifecycleService ,
4344 type ExecutionHandle ,
@@ -1541,30 +1542,45 @@ export class ShellExecutionService {
15411542 }
15421543
15431544 const activePty = this . activePtys . get ( pid ) ;
1544- if ( activePty ) {
1545+ if ( ! activePty ) {
1546+ return ;
1547+ }
1548+
1549+ // Skip Windows: process.kill(pid, 0) is heavy and native errors are catchable there.
1550+ if ( process . platform !== 'win32' ) {
15451551 try {
1546- activePty . ptyProcess . resize ( cols , rows ) ;
1547- activePty . headlessTerminal . resize ( cols , rows ) ;
1552+ process . kill ( pid , 0 ) ;
15481553 } catch ( e ) {
1549- // Ignore errors if the pty has already exited, which can happen
1550- // due to a race condition between the exit event and this call.
1551- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1552- const err = e as { code ?: string ; message ?: string } ;
1553- const isEsrch = err . code === 'ESRCH' ;
1554- const isWindowsPtyError = err . message ?. includes (
1555- 'Cannot resize a pty that has already exited' ,
1556- ) ;
1557-
1558- if ( isEsrch || isWindowsPtyError ) {
1559- // On Unix, we get an ESRCH error.
1560- // On Windows, we get a message-based error.
1561- // In both cases, it's safe to ignore.
1562- } else {
1563- throw e ;
1554+ // Bail only if the process is explicitly confirmed dead (ESRCH).
1555+ if ( isNodeError ( e ) && e . code === 'ESRCH' ) {
1556+ return ;
15641557 }
15651558 }
15661559 }
15671560
1561+ try {
1562+ activePty . ptyProcess . resize ( cols , rows ) ;
1563+ activePty . headlessTerminal . resize ( cols , rows ) ;
1564+ } catch ( e ) {
1565+ // Ignore errors if the pty has already exited, which can happen
1566+ // due to a race condition between the exit event and this call.
1567+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1568+ const err = e as { code ?: string ; message ?: string } ;
1569+ const isEsrch = err . code === 'ESRCH' ;
1570+ const isEbadf = err . code === 'EBADF' || err . message ?. includes ( 'EBADF' ) ;
1571+ const isWindowsPtyError = err . message ?. includes (
1572+ 'Cannot resize a pty that has already exited' ,
1573+ ) ;
1574+
1575+ if ( isEsrch || isEbadf || isWindowsPtyError ) {
1576+ // On Unix, we get an ESRCH or EBADF error.
1577+ // On Windows, we get a message-based error.
1578+ // In both cases, it's safe to ignore.
1579+ } else {
1580+ throw e ;
1581+ }
1582+ }
1583+
15681584 // Force emit the new state after resize
15691585 if ( activePty ) {
15701586 const endLine = activePty . headlessTerminal . buffer . active . length ;
0 commit comments