Summary
apps/desktop/scripts/electron-dev.mjs hardcodes Vite dev port to 5173 with strictPort: true and treats any TCP listener on 5173 as "Vite is ready". When another process holds the port, Electron's loadURL either renders the stranger's content or Vite crashes on spawn.
Reproduction
- Hold port 5173 with a non-Vite process — either of:
python3 -m http.server 5173 (HTTP — clearest visual)
node -e \"require('net').createServer(()=>{}).listen(5173,'0.0.0.0')\" (raw TCP)
- Run
pnpm dev (or pnpm --filter @openwork/desktop dev).
Observed
- Electron window opens and loads whatever the stranger serves (e.g. python
directory listing page) instead of the openwork app.
- With raw TCP blocker: launcher hangs ~12s on
looksLikeVite fetches (4s × 3 candidate URLs), then Vite child fails to bind 5173 due to strictPort: true, dev session exits.
Root causes
electron-dev.mjs line ~205 (pre-fix): portIsOpenForVite fallback flips viteReady = true when anything listens on the port — not just real Vite.
- No port-availability check before spawning Vite child;
strictPort: true in apps/app/vite.config.ts:99 blocks Vite from picking the next port itself.
OPENWORK_ELECTRON_START_URL always built from the hardcoded default port, no rebuild after collision.
- (Side issue)
fetchWithTimeout 4s default × multiple candidate URLs causes long hangs against non-HTTP TCP listeners.
Expected
Either:
- Detect port is busy, auto-bump to the next free port, log it, spawn Vite there, build
startUrl from the chosen port; or
- If user explicitly set
PORT=, fail fast with a clear error rather than silently bumping.
Environment
- macOS (Darwin 25.4.0)
- Reproduces on
upstream/dev at the time of writing.
Summary
apps/desktop/scripts/electron-dev.mjshardcodes Vite dev port to 5173 withstrictPort: trueand treats any TCP listener on 5173 as "Vite is ready". When another process holds the port, Electron'sloadURLeither renders the stranger's content or Vite crashes on spawn.Reproduction
python3 -m http.server 5173(HTTP — clearest visual)node -e \"require('net').createServer(()=>{}).listen(5173,'0.0.0.0')\"(raw TCP)pnpm dev(orpnpm --filter @openwork/desktop dev).Observed
directory listingpage) instead of the openwork app.looksLikeVitefetches (4s × 3 candidate URLs), then Vite child fails to bind 5173 due tostrictPort: true, dev session exits.Root causes
electron-dev.mjsline ~205 (pre-fix):portIsOpenForVitefallback flipsviteReady = truewhen anything listens on the port — not just real Vite.strictPort: trueinapps/app/vite.config.ts:99blocks Vite from picking the next port itself.OPENWORK_ELECTRON_START_URLalways built from the hardcoded default port, no rebuild after collision.fetchWithTimeout4s default × multiple candidate URLs causes long hangs against non-HTTP TCP listeners.Expected
Either:
startUrlfrom the chosen port; orPORT=, fail fast with a clear error rather than silently bumping.Environment
upstream/devat the time of writing.