From f825dff6c721d230db9727cfe3f9ec8fac5a692d Mon Sep 17 00:00:00 2001 From: Priyanshu Choudhary <57816400+Priyanchew@users.noreply.github.com> Date: Mon, 4 May 2026 22:33:33 +0530 Subject: [PATCH] fix(runtime-process): preserve EPERM in Windows pty-host sweep exit-poll The catch in sweepWindowsPtyHosts treated every error as "process exited", including EPERM. On Windows EPERM means the pty-host exists but the caller lacks permission to signal it (cross-context), so the orphan was skipping the killProcessTree force-kill step and leaking. Mirror the destroy() logic at line 290: only flag exited on non-EPERM (typically ESRCH). Co-Authored-By: Claude Sonnet 4.6 --- packages/plugins/runtime-process/src/index.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/plugins/runtime-process/src/index.ts b/packages/plugins/runtime-process/src/index.ts index 5cdb869b6..0d0c1925f 100644 --- a/packages/plugins/runtime-process/src/index.ts +++ b/packages/plugins/runtime-process/src/index.ts @@ -525,8 +525,13 @@ export async function sweepWindowsPtyHosts(): Promise<{ while (Date.now() < deadline) { try { process.kill(entry.ptyHostPid, 0); - } catch { - exited = true; + } catch (err: unknown) { + // EPERM: process exists but we can't signal it (cross-context on + // Windows). It is NOT gone — fall through to force-kill. Any other + // code (typically ESRCH) means it has already exited. + if ((err as { code?: string }).code !== "EPERM") { + exited = true; + } break; } await new Promise((r) => setTimeout(r, 25));