From d6705fbfb56fb48d9eec816f479e852bfed7f92b Mon Sep 17 00:00:00 2001 From: "Anand (Andy) Houston" Date: Tue, 30 Dec 2025 16:47:29 +0800 Subject: [PATCH 1/2] fix(windows): properly kill server process tree on app quit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Windows, serverProcess.kill() doesn't reliably terminate Node.js child processes. This causes orphaned node processes to hold onto ports 3007/3008, preventing the app from starting on subsequent launches. Use taskkill with /f /t flags to force-kill the entire process tree on Windows, while keeping SIGTERM for macOS/Linux where it works correctly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- apps/ui/src/main.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/ui/src/main.ts b/apps/ui/src/main.ts index 4e112f25..d227866f 100644 --- a/apps/ui/src/main.ts +++ b/apps/ui/src/main.ts @@ -595,9 +595,15 @@ app.on('window-all-closed', () => { }); app.on('before-quit', () => { - if (serverProcess) { + if (serverProcess && serverProcess.pid) { console.log('[Electron] Stopping server...'); - serverProcess.kill(); + if (process.platform === 'win32') { + // Windows: use taskkill with /t to kill entire process tree + // This prevents orphaned node processes when closing the app + spawn('taskkill', ['/f', '/t', '/pid', serverProcess.pid.toString()]); + } else { + serverProcess.kill('SIGTERM'); + } serverProcess = null; } From 784d7fc05928b8578fbae1251283c2a7a736e57c Mon Sep 17 00:00:00 2001 From: "Anand (Andy) Houston" Date: Tue, 30 Dec 2025 16:56:31 +0800 Subject: [PATCH 2/2] fix(windows): use execSync for reliable process termination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address code review feedback: - Replace async spawn() with sync execSync() to ensure taskkill completes before app exits - Add try/catch error handling for permission/invalid-PID errors - Add helpful error logging for debugging 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- apps/ui/src/main.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/ui/src/main.ts b/apps/ui/src/main.ts index d227866f..b2f7bd86 100644 --- a/apps/ui/src/main.ts +++ b/apps/ui/src/main.ts @@ -6,7 +6,7 @@ */ import path from 'path'; -import { spawn, ChildProcess } from 'child_process'; +import { spawn, execSync, ChildProcess } from 'child_process'; import fs from 'fs'; import crypto from 'crypto'; import http, { Server } from 'http'; @@ -598,9 +598,14 @@ app.on('before-quit', () => { if (serverProcess && serverProcess.pid) { console.log('[Electron] Stopping server...'); if (process.platform === 'win32') { - // Windows: use taskkill with /t to kill entire process tree - // This prevents orphaned node processes when closing the app - spawn('taskkill', ['/f', '/t', '/pid', serverProcess.pid.toString()]); + try { + // Windows: use taskkill with /t to kill entire process tree + // This prevents orphaned node processes when closing the app + // Using execSync to ensure process is killed before app exits + execSync(`taskkill /f /t /pid ${serverProcess.pid}`, { stdio: 'ignore' }); + } catch (error) { + console.error('[Electron] Failed to kill server process:', (error as Error).message); + } } else { serverProcess.kill('SIGTERM'); }