fix(ports): stop spurious "Failed to close N port(s)" toast on close-all
Summary
- Closing several ports at once (the workspace "Close all ports" X) succeeded but spuriously toasted "Failed to close 1 port — Port not found in tracked ports."
PortManager.killPortnow treats a no-longer-tracked port as already closed → success, eliminating the false-failure toast. Fixes both desktop-local and host-service kill paths (sharedport-scannerpackage).
Why / Context
"Close all" calls killPorts(group.ports), which fires every kill concurrently (Promise.all). The first kill runs treeKillWithEscalation(pid), which tears down the entire process tree — so when two of a workspace's ports share a process tree (common: a dev server plus its HMR/websocket/inspector port), killing one also kills the process behind the sibling port. A port scan (the periodic interval, or a hint scan triggered by the dying process's terminal output) then removes the now-dead sibling from the tracked ports map.
By the time the sibling's own kill call reaches its synchronous map lookup, the entry is gone, so it returned { success: false, error: "Port not found in tracked ports" }. The renderer counted one failed result and showed the toast — even though the port really was closed, which is why it "seems to work fine."
How It Works
A port that is no longer tracked is, by definition, already closed — exactly the outcome a kill aims for. The !detectedPort branch in killPort now returns { success: true } instead of an error. This mirrors treeKillWithEscalation, which already treats an already-dead process (ESRCH) as success; the change just aligns the map-lookup path with that behavior. The workspace-mismatch and shell-process guards are unchanged.
Manual QA Checklist
- Start a workspace with a dev server that listens on multiple ports (e.g. Vite/Next with HMR).
- Click the workspace "Close all ports" X — ports close and no error toast appears.
- Single per-port X still closes the port with no error toast.
- Closing a port that is still genuinely listening returns success (process killed).
Not yet verified live in the running app — this is a code-path fix with unit coverage. Happy to run /verify against the desktop app if desired.
Testing
bun test packages/port-scanner/src/port-manager.test.ts— 26 pass (4 newkillPortregression tests: tracked kill, untracked = success, shell-process guard, workspace mismatch)bun run lintbun run typecheck
Risks / Rollback
- Risk: low. Narrows a single error path to success; the kill itself (
treeKillWithEscalation) is unchanged and already idempotent for dead processes. The only behavior change is that a kill targeting an untracked port no longer reports failure. - Rollback: revert this commit.
Summary by cubic
Stops the false "Failed to close N port(s)" toast when using Close all ports. PortManager.killPort now treats an untracked port as already closed so concurrent kills complete without errors.
- Bug Fixes
- Sibling kills after a shared process tree is terminated now return success instead of "Port not found in tracked ports".
- Applies to both desktop-local and host-service paths via shared
port-scanner.
Summary by CodeRabbit
-
Bug Fixes
- Updated port termination logic to report success when targeting an untracked port, rather than reporting failure.
-
Tests
- Added comprehensive test coverage for port termination scenarios, including successful termination, untracked port handling, shell process protection, and workspace validation checks.