authorgravatar for albertli_042@hotmail.comAlb11747 <albertli_042@hotmail.com> 2026-10-11 13:14:36-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-11 10:14:36-07:00
log243c9994d047f3102a9622d1a5cff42ef78515ee
tree4159f7c31a8dfb7ee01ce4bb60b2ff9817c6ed02
parentc09c235129c1581f92a4f6e30470461cf7777f05
signature Signed by PGP key B5690EEEBB952194

fix(server): skip Windows cleanup for exited child processes (#16007)


3 files changed, 65 insertions(+), 3 deletions(-)

patches/@effect__platform-node-shared@4.0.2.patch created+60
......@@ -0,0 +1,60 @@
1diff --git a/dist/NodeChildProcessSpawner.js b/dist/NodeChildProcessSpawner.js
2--- a/dist/NodeChildProcessSpawner.js
3+++ b/dist/NodeChildProcessSpawner.js
4@@ -44,9 +44,17 @@
5 return false;
6 }
7 };
8-const taskkill = (childProcess, onExit = () => {}) => NodeChildProcess.execFile("taskkill", ["/pid", String(childProcess.pid), "/T", "/F"], {
9- windowsHide: true
10-}, onExit);
11+const taskkill = (childProcess, onExit = () => {}) => {
12+ // Once Node observes the leader's exit, taskkill /T cannot reclaim orphaned
13+ // descendants through its PID, which may be reused. Skip stale-PID cleanup.
14+ if (childProcess.exitCode !== null || childProcess.signalCode !== null) {
15+ onExit(null);
16+ return;
17+ }
18+ NodeChildProcess.execFile("taskkill", ["/pid", String(childProcess.pid), "/T", "/F"], {
19+ windowsHide: true
20+ }, onExit);
21+};
22 const make = /*#__PURE__*/Effect.gen(function* () {
23 const fs = yield* FileSystem.FileSystem;
24 const path = yield* Path.Path;
25diff --git a/src/NodeChildProcessSpawner.ts b/src/NodeChildProcessSpawner.ts
26--- a/src/NodeChildProcessSpawner.ts
27+++ b/src/NodeChildProcessSpawner.ts
28@@ -19,7 +19,9 @@
29 * `forceKillAfter`, the wait is limited to one second and never escalates.
30 * With it, cleanup may take the configured duration plus a final one-second
31 * wait after `SIGKILL`. Zombie descendants can consume either full bound. On
32- * Windows, `taskkill` terminates the tree and only the leader's exit is awaited.
33+ * Windows, `taskkill` terminates the tree while the leader is running and only
34+ * the leader's exit is awaited. Descendants that outlive the leader are not
35+ * reclaimed by PID-based cleanup.
36 *
37 * POSIX cleanup targets a numeric process-group ID. If the group disappears
38 * and its ID is reused before cleanup, an unrelated group may be signalled.
39@@ -99,13 +101,20 @@
40 const taskkill = (
41 childProcess: NodeChildProcess.ChildProcess,
42 onExit: (error: NodeChildProcess.ExecException | null) => void = () => {}
43-) =>
44+) => {
45+ // Once Node observes the leader's exit, taskkill /T cannot reclaim orphaned
46+ // descendants through its PID, which may be reused. Skip stale-PID cleanup.
47+ if (childProcess.exitCode !== null || childProcess.signalCode !== null) {
48+ onExit(null)
49+ return
50+ }
51 NodeChildProcess.execFile(
52 "taskkill",
53 ["/pid", String(childProcess.pid!), "/T", "/F"],
54 { windowsHide: true },
55 onExit
56 )
57+}
58
59 const make = Effect.gen(function*() {
60 const fs = yield* FileSystem.FileSystem
pnpm-lock.yaml+4-3
......@@ -144,6 +144,7 @@ packageExtensionsChecksum: sha256-FYDaKefjFcda1+vgiq8yMpZC4ehE5CyLi/bsCGLgTtY=
144144
145145patchedDependencies:
146146 '@clerk/expo@4.6.8': b8b22ab733de5bea846ad9e88046c10582352fe9ad86f314f368b2a823f0f281
147 '@effect/platform-node-shared@4.0.2': 4efe73732a53b7d7bdbd9a9263eed06310fc387f870ef4a5e66429e9d1fd7cd9
147148 '@effect/vitest@4.0.2': 595b7d26fb635caba9c3d39fcb189bb78a000192f8d3b60ead7174b310d1ade9
148149 '@expo/metro-config@58.0.6': 90fa980e8b6480631fb2fbb04559834e3c612db40c550cd828f11be297d8d760
149150 '@ff-labs/fff-node@0.11.0': 8956d6ed6938a1bce69b2d0b067ff258dd9ca753c5bbe7006f7b68f760ea3ad2
......@@ -611,7 +612,7 @@ importers:
611612 version: 4.0.2(bufferutil@4.1.0)(effect@4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688))(redis@6.2.1)(utf-8-validate@6.0.6)
612613 '@effect/platform-node-shared':
613614 specifier: 4.0.2
614 version: 4.0.2(bufferutil@4.1.0)(effect@4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688))(utf-8-validate@6.0.6)
615 version: 4.0.2(patch_hash=4efe73732a53b7d7bdbd9a9263eed06310fc387f870ef4a5e66429e9d1fd7cd9)(bufferutil@4.1.0)(effect@4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688))(utf-8-validate@6.0.6)
615616 '@ff-labs/fff-node':
616617 specifier: 0.11.0
617618 version: 0.11.0(patch_hash=8956d6ed6938a1bce69b2d0b067ff258dd9ca753c5bbe7006f7b68f760ea3ad2)
......@@ -13838,7 +13839,7 @@ snapshots:
1383813839 transitivePeerDependencies:
1383913840 - encoding
1384013841
13841 '@effect/platform-node-shared@4.0.2(bufferutil@4.1.0)(effect@4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688))(utf-8-validate@6.0.6)':
13842 '@effect/platform-node-shared@4.0.2(patch_hash=4efe73732a53b7d7bdbd9a9263eed06310fc387f870ef4a5e66429e9d1fd7cd9)(bufferutil@4.1.0)(effect@4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688))(utf-8-validate@6.0.6)':
1384213843 dependencies:
1384313844 '@types/ws': 8.18.2
1384413845 effect: 4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688)
......@@ -13849,7 +13850,7 @@ snapshots:
1384913850
1385013851 '@effect/platform-node@4.0.2(bufferutil@4.1.0)(effect@4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688))(redis@6.2.1)(utf-8-validate@6.0.6)':
1385113852 dependencies:
13852 '@effect/platform-node-shared': 4.0.2(bufferutil@4.1.0)(effect@4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688))(utf-8-validate@6.0.6)
13853 '@effect/platform-node-shared': 4.0.2(patch_hash=4efe73732a53b7d7bdbd9a9263eed06310fc387f870ef4a5e66429e9d1fd7cd9)(bufferutil@4.1.0)(effect@4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688))(utf-8-validate@6.0.6)
1385313854 effect: 4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688)
1385413855 redis: 6.2.1
1385513856 undici: 8.11.2
pnpm-workspace.yaml+1
......@@ -288,6 +288,7 @@ patchedDependencies:
288288 expo-blur@58.0.3: patches/expo-blur@58.0.3.patch
289289 expo-audio@58.0.4: patches/expo-audio@58.0.4.patch
290290 "@clerk/expo@4.6.8": patches/@clerk__expo@4.6.8.patch
291 "@effect/platform-node-shared@4.0.2": patches/@effect__platform-node-shared@4.0.2.patch
291292 "@effect/vitest@4.0.2": patches/@effect__vitest@4.0.2.patch
292293 "@expo/metro-config@58.0.6": patches/@expo__metro-config@58.0.6.patch
293294 expo-sharing@58.0.13: patches/expo-sharing@58.0.13.patch