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=...@@ -144,6 +144,7 @@ packageExtensionsChecksum: sha256-FYDaKefjFcda1+vgiq8yMpZC4ehE5CyLi/bsCGLgTtY=
144144
145patchedDependencies:145patchedDependencies:
146 '@clerk/expo@4.6.8': b8b22ab733de5bea846ad9e88046c10582352fe9ad86f314f368b2a823f0f281146 '@clerk/expo@4.6.8': b8b22ab733de5bea846ad9e88046c10582352fe9ad86f314f368b2a823f0f281
147 '@effect/platform-node-shared@4.0.2': 4efe73732a53b7d7bdbd9a9263eed06310fc387f870ef4a5e66429e9d1fd7cd9
147 '@effect/vitest@4.0.2': 595b7d26fb635caba9c3d39fcb189bb78a000192f8d3b60ead7174b310d1ade9148 '@effect/vitest@4.0.2': 595b7d26fb635caba9c3d39fcb189bb78a000192f8d3b60ead7174b310d1ade9
148 '@expo/metro-config@58.0.6': 90fa980e8b6480631fb2fbb04559834e3c612db40c550cd828f11be297d8d760149 '@expo/metro-config@58.0.6': 90fa980e8b6480631fb2fbb04559834e3c612db40c550cd828f11be297d8d760
149 '@ff-labs/fff-node@0.11.0': 8956d6ed6938a1bce69b2d0b067ff258dd9ca753c5bbe7006f7b68f760ea3ad2150 '@ff-labs/fff-node@0.11.0': 8956d6ed6938a1bce69b2d0b067ff258dd9ca753c5bbe7006f7b68f760ea3ad2
...@@ -611,7 +612,7 @@ importers:...@@ -611,7 +612,7 @@ importers:
611 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)612 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)
612 '@effect/platform-node-shared':613 '@effect/platform-node-shared':
613 specifier: 4.0.2614 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)
615 '@ff-labs/fff-node':616 '@ff-labs/fff-node':
616 specifier: 0.11.0617 specifier: 0.11.0
617 version: 0.11.0(patch_hash=8956d6ed6938a1bce69b2d0b067ff258dd9ca753c5bbe7006f7b68f760ea3ad2)618 version: 0.11.0(patch_hash=8956d6ed6938a1bce69b2d0b067ff258dd9ca753c5bbe7006f7b68f760ea3ad2)
...@@ -13838,7 +13839,7 @@ snapshots:...@@ -13838,7 +13839,7 @@ snapshots:
13838 transitivePeerDependencies:13839 transitivePeerDependencies:
13839 - encoding13840 - 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)':
13842 dependencies:13843 dependencies:
13843 '@types/ws': 8.18.213844 '@types/ws': 8.18.2
13844 effect: 4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688)13845 effect: 4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688)
...@@ -13849,7 +13850,7 @@ snapshots:...@@ -13849,7 +13850,7 @@ snapshots:
1384913850
13850 '@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)':13851 '@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)':
13851 dependencies:13852 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)
13853 effect: 4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688)13854 effect: 4.0.2(patch_hash=ec61ee307eaec3e17f757e0e6a16296ea4b7a8c3188df0c6ec5e66301fd78688)
13854 redis: 6.2.113855 redis: 6.2.1
13855 undici: 8.11.213856 undici: 8.11.2
pnpm-workspace.yaml+1
...@@ -288,6 +288,7 @@ patchedDependencies:...@@ -288,6 +288,7 @@ patchedDependencies:
288 expo-blur@58.0.3: patches/expo-blur@58.0.3.patch288 expo-blur@58.0.3: patches/expo-blur@58.0.3.patch
289 expo-audio@58.0.4: patches/expo-audio@58.0.4.patch289 expo-audio@58.0.4: patches/expo-audio@58.0.4.patch
290 "@clerk/expo@4.6.8": patches/@clerk__expo@4.6.8.patch290 "@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
291 "@effect/vitest@4.0.2": patches/@effect__vitest@4.0.2.patch292 "@effect/vitest@4.0.2": patches/@effect__vitest@4.0.2.patch
292 "@expo/metro-config@58.0.6": patches/@expo__metro-config@58.0.6.patch293 "@expo/metro-config@58.0.6": patches/@expo__metro-config@58.0.6.patch
293 expo-sharing@58.0.13: patches/expo-sharing@58.0.13.patch294 expo-sharing@58.0.13: patches/expo-sharing@58.0.13.patch