From a192b2ea52b3166a7d190bf5f60f3feb030306bb Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 21 Jun 2026 10:57:35 +0200 Subject: [PATCH] fix(windows): resolve taskkill in core spawns --- src/cli/update-cli/update-command.ts | 13 +++++++++---- src/process/exec.ts | 18 ++++++++++++------ src/process/exec.windows.test.ts | 3 ++- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/cli/update-cli/update-command.ts b/src/cli/update-cli/update-command.ts index c7f7fb99b04..dd550bd191a 100644 --- a/src/cli/update-cli/update-command.ts +++ b/src/cli/update-cli/update-command.ts @@ -100,6 +100,7 @@ import { type PreUpdateConfigRestoreInput, } from "../../infra/update-post-core-context.js"; import { runGatewayUpdate, type UpdateRunResult } from "../../infra/update-runner.js"; +import { getWindowsSystem32ExePath } from "../../infra/windows-install-roots.js"; import { normalizePluginsConfig, resolveEffectiveEnableState } from "../../plugins/config-state.js"; import { loadInstalledPluginIndexInstallRecords, @@ -2877,10 +2878,14 @@ async function readPostCorePluginUpdateResultFile( function stopPostCoreUpdateChild(child: ChildProcess): void { if (process.platform === "win32" && child.pid) { try { - const killer = spawn("taskkill", ["/PID", String(child.pid), "/T", "/F"], { - stdio: "ignore", - windowsHide: true, - }); + const killer = spawn( + getWindowsSystem32ExePath("taskkill.exe"), + ["/PID", String(child.pid), "/T", "/F"], + { + stdio: "ignore", + windowsHide: true, + }, + ); killer.once("error", () => { child.kill(); }); diff --git a/src/process/exec.ts b/src/process/exec.ts index 6dcc26dd7ee..369a41b62bc 100644 --- a/src/process/exec.ts +++ b/src/process/exec.ts @@ -11,6 +11,7 @@ import { decodeWindowsOutputBuffer, resolveWindowsConsoleEncoding, } from "../infra/windows-encoding.js"; +import { getWindowsSystem32ExePath } from "../infra/windows-install-roots.js"; import { logDebug, logError } from "../logger.js"; import { killProcessTree as terminateProcessTree } from "./kill-tree.js"; import { resolveCommandStdio } from "./spawn-utils.js"; @@ -430,8 +431,9 @@ export async function runCommandWithTimeout( } if (killProcessTree && typeof child.pid === "number" && child.pid > 0) { if (process.platform === "win32") { + const taskkillPath = getWindowsSystem32ExePath("taskkill.exe"); try { - spawn("taskkill", ["/PID", String(child.pid), "/T"], { + spawn(taskkillPath, ["/PID", String(child.pid), "/T"], { stdio: "ignore", windowsHide: true, }); @@ -447,7 +449,7 @@ export async function runCommandWithTimeout( return; } try { - spawn("taskkill", ["/PID", String(child.pid), "/T", "/F"], { + spawn(taskkillPath, ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, }); @@ -467,10 +469,14 @@ export async function runCommandWithTimeout( } if (process.platform === "win32" && typeof child.pid === "number" && child.pid > 0) { try { - spawn("taskkill", ["/PID", String(child.pid), "/T", "/F"], { - stdio: "ignore", - windowsHide: true, - }); + spawn( + getWindowsSystem32ExePath("taskkill.exe"), + ["/PID", String(child.pid), "/T", "/F"], + { + stdio: "ignore", + windowsHide: true, + }, + ); return; } catch { // Fall through to Node's direct child kill as a last resort. diff --git a/src/process/exec.windows.test.ts b/src/process/exec.windows.test.ts index da27336f392..ede5a1a3d99 100644 --- a/src/process/exec.windows.test.ts +++ b/src/process/exec.windows.test.ts @@ -7,6 +7,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite import { resetWindowsInstallRootsForTests, getWindowsInstallRoots, + getWindowsSystem32ExePath, } from "../infra/windows-install-roots.js"; import { withMockedWindowsPlatform, withRestoredMocks } from "../test-utils/vitest-spies.js"; @@ -496,7 +497,7 @@ describe("windows command wrapper behavior", () => { expect(child.kill).not.toHaveBeenCalled(); expect(spawnMock).toHaveBeenCalledTimes(2); const taskkillCall = requireSpawnCall(1); - expect(taskkillCall[0]).toBe("taskkill"); + expect(taskkillCall[0]).toBe(getWindowsSystem32ExePath("taskkill.exe")); expect(taskkillCall[1]).toEqual(["/PID", "1234", "/T", "/F"]); expect(taskkillCall[2]).toEqual({ stdio: "ignore",