@@ -37,6 +37,7 @@ import {
3737} from './sandboxManager.js' ;
3838import type { SandboxConfig } from '../config/config.js' ;
3939import { killProcessGroup } from '../utils/process-utils.js' ;
40+ import { isNodeError } from '../utils/errors.js' ;
4041import {
4142 ExecutionLifecycleService ,
4243 type ExecutionHandle ,
@@ -1507,31 +1508,45 @@ export class ShellExecutionService {
15071508 }
15081509
15091510 const activePty = this . activePtys . get ( pid ) ;
1510- if ( activePty ) {
1511+ if ( ! activePty ) {
1512+ return ;
1513+ }
1514+
1515+ // Skip Windows: process.kill(pid, 0) is heavy and native errors are catchable there.
1516+ if ( process . platform !== 'win32' ) {
15111517 try {
1512- activePty . ptyProcess . resize ( cols , rows ) ;
1513- activePty . headlessTerminal . resize ( cols , rows ) ;
1518+ process . kill ( pid , 0 ) ;
15141519 } catch ( e ) {
1515- // Ignore errors if the pty has already exited, which can happen
1516- // due to a race condition between the exit event and this call.
1517- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1518- const err = e as { code ?: string ; message ?: string } ;
1519- const isEsrch = err . code === 'ESRCH' ;
1520- const isEbadf = err . code === 'EBADF' || err . message ?. includes ( 'EBADF' ) ;
1521- const isWindowsPtyError = err . message ?. includes (
1522- 'Cannot resize a pty that has already exited' ,
1523- ) ;
1524-
1525- if ( isEsrch || isEbadf || isWindowsPtyError ) {
1526- // On Unix, we get an ESRCH or EBADF error.
1527- // On Windows, we get a message-based error.
1528- // In both cases, it's safe to ignore.
1529- } else {
1530- throw e ;
1520+ // Bail only if the process is explicitly confirmed dead (ESRCH).
1521+ if ( isNodeError ( e ) && e . code === 'ESRCH' ) {
1522+ return ;
15311523 }
15321524 }
15331525 }
15341526
1527+ try {
1528+ activePty . ptyProcess . resize ( cols , rows ) ;
1529+ activePty . headlessTerminal . resize ( cols , rows ) ;
1530+ } catch ( e ) {
1531+ // Ignore errors if the pty has already exited, which can happen
1532+ // due to a race condition between the exit event and this call.
1533+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1534+ const err = e as { code ?: string ; message ?: string } ;
1535+ const isEsrch = err . code === 'ESRCH' ;
1536+ const isEbadf = err . code === 'EBADF' || err . message ?. includes ( 'EBADF' ) ;
1537+ const isWindowsPtyError = err . message ?. includes (
1538+ 'Cannot resize a pty that has already exited' ,
1539+ ) ;
1540+
1541+ if ( isEsrch || isEbadf || isWindowsPtyError ) {
1542+ // On Unix, we get an ESRCH or EBADF error.
1543+ // On Windows, we get a message-based error.
1544+ // In both cases, it's safe to ignore.
1545+ } else {
1546+ throw e ;
1547+ }
1548+ }
1549+
15351550 // Force emit the new state after resize
15361551 if ( activePty ) {
15371552 const endLine = activePty . headlessTerminal . buffer . active . length ;
0 commit comments