Skip to content

fix(gateway): ride out self-update port race + clearer update restart UX - #173

Merged
its-mash merged 1 commit into
mainfrom
fix/update-restart-port-race
Jun 16, 2026
Merged

fix(gateway): ride out self-update port race + clearer update restart UX#173
its-mash merged 1 commit into
mainfrom
fix/update-restart-port-race

Conversation

@its-mash

Copy link
Copy Markdown
Member

Problem

Two issues reported around the in-place self-update / restart flow on Windows:

  1. "Gateway port not available" after an update. When the app updates itself and relaunches, the new build starts before the prior process has finished releasing its gateway listener on 45818. Auto-start did a single port probe (lib.rs), saw the port momentarily busy, and immediately deferred to the user with a port-conflict prompt — even though, as the reporter observed, the port frees within a second or two (manually starting on the same port right after just works).
  2. The manual "Check for Updates" → install flow feels like a crash. On Windows the passive NSIS installer kills the app to apply the update, so the window just vanishes with no "we're restarting" signal — and then the relaunch greets you with the spurious port error above.

Fix

Backend — ride out the restart race (mcpmux-core, lib.rs)

  • New wait_for_port_available(port, timeout) in gateway_port_service: re-probes a busy port every 400ms for up to AUTOSTART_PORT_WAIT (~6s) before giving up. The common case (port already free) returns on the first synchronous probe with no sleep.
  • Auto-start now calls it instead of a one-shot is_port_available. A genuine conflict (another app permanently owning the port) stays busy the whole window and still surfaces the prompt — only the transient self-update race is absorbed.

Frontend — keep the resolver polling long enough (AutoStartConflictResolver)

  • Extended the poll schedule (~10s total) so the UI is still polling when the backend resolves at the end of its wait window. Otherwise a late-raised conflict would never reach the prompt.

UX — make the restart expected (UpdateChecker)

  • The install flow flips to an explicit "Installing v X — McpMux will close and reopen automatically. This is expected; the app isn't crashing." notice the moment the download finishes (before the installer kills the app), and the button reads "Restarting…".

Tests

  • New unit tests for wait_for_port_available: free (immediate), held (gives up), and freed-mid-wait (succeeds).
  • cargo nextest -p mcpmux-core (139 passed), cargo clippy --workspace -D warnings, cargo fmt --check, pnpm typecheck, App.test.tsx (12 passed), ESLint — all green.

Notes

  • The startup auto-update path (silent download+install+relaunch when auto-install is on) is intentionally left unchanged — the reporter confirmed that path feels fine.

After an in-place self-update relaunches the app, the freshly launched
build raced the prior process's gateway-listener teardown. Auto-start did
a single port probe, saw 45818 momentarily busy, and immediately deferred
to the user with a "port not available" conflict prompt — even though the
port frees within a second or two (the user could manually start on the
same port right after). The abrupt close + spurious error made a normal
update read as a crash.

Backend: auto-start now waits up to ~6s (probing every 400ms) for the
preferred port to free before treating it as a real conflict. The common
case (port free) still returns on the first synchronous probe with no
sleep. A genuine conflict (another app owning the port) stays busy the
whole window and still surfaces the prompt. Added `wait_for_port_available`
to the port service with unit tests for the free / held / freed-mid-wait
cases.

Frontend: extended AutoStartConflictResolver's poll schedule (~10s) so it
is still polling when the backend resolves at the end of its wait window —
otherwise a late conflict would never reach the prompt.

UX: the manual "Download and Install" flow now flips to an explicit
"Installing — McpMux will close and reopen automatically, this is expected"
notice the moment the download finishes, so the window vanishing on Windows
(passive NSIS installer kills the app) no longer looks like a crash.

Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
@its-mash
its-mash merged commit 6868992 into main Jun 16, 2026
13 checks passed
@its-mash
its-mash deleted the fix/update-restart-port-race branch June 16, 2026 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant