fix(lsp): terminate LSP child process trees on failure#607
fix(lsp): terminate LSP child process trees on failure#607Olusammytee wants to merge 1 commit intoKilo-Org:devfrom
Conversation
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Clean, focused change that replaces direct The Files Reviewed (1 files)
|
There was a problem hiding this comment.
Pull request overview
Updates LSP startup failure handling to terminate entire spawned process trees (especially on Windows) so failed LSP startups don’t leave orphaned/zombie language server processes behind.
Changes:
- Replace
ChildProcess.kill()calls in LSP client initialization failure paths withShell.killTree(...). - Use a process “exited” predicate to avoid attempting to kill already-terminated processes.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| }).catch((err) => { | ||
| s.broken.add(key) | ||
| handle.process.kill() | ||
| void Shell.killTree(handle.process, { exited: () => handle.process.exitCode !== null }) |
There was a problem hiding this comment.
The exited predicate only checks exitCode !== null, but child processes can also exit via signal (where signalCode is set and exitCode may remain null). That can cause Shell.killTree to unnecessarily attempt SIGKILL/taskkill after the process already exited. Consider treating the process as exited when either exitCode or signalCode is non-null (or attach an exit listener and use a boolean, like other killTree call sites).
| if (!client) { | ||
| handle.process.kill() | ||
| void Shell.killTree(handle.process, { exited: () => handle.process.exitCode !== null }) | ||
| return undefined |
There was a problem hiding this comment.
Shell.killTree tries process.kill(-pid, ...) on non-Windows to signal the process group, but LSP servers in this file are spawned without detached: true (so pid typically isn't a process group id). In practice this means the group kill will likely throw and killTree will fall back to proc.kill(), which won’t reliably terminate child processes on POSIX. If the goal is to kill process trees cross-platform, spawn LSP servers in their own process group (e.g., detached: process.platform !== "win32") so the negative-PID kill path actually works.
| }).catch((err) => { | ||
| s.broken.add(key) | ||
| handle.process.kill() | ||
| void Shell.killTree(handle.process, { exited: () => handle.process.exitCode !== null }) | ||
| log.error(`Failed to initialize LSP client ${server.id}`, { error: err }) | ||
| return undefined | ||
| }) | ||
|
|
||
| if (!client) { | ||
| handle.process.kill() | ||
| void Shell.killTree(handle.process, { exited: () => handle.process.exitCode !== null }) |
There was a problem hiding this comment.
This change introduces new failure-path behavior (killing the spawned LSP process tree) but there isn’t a unit test verifying that Shell.killTree is invoked when LSPClient.create fails / when client is undefined. Since the repo already has LSP-related tests (e.g., test/lsp/client.test.ts), it would be good to add a focused test that stubs Shell.killTree and asserts it’s called in these startup failure paths.
Summary
Fixes #591