From 322a880a02a20920ef94b2d2c6c43fd33c7c3866 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Sun, 8 Feb 2026 20:44:43 +0000 Subject: [PATCH] fix(dev): avoid localhost dual-stack collisions --- packages/electron-app/electron/main/process-manager.ts | 3 +++ packages/server/src/index.ts | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/electron-app/electron/main/process-manager.ts b/packages/electron-app/electron/main/process-manager.ts index bcf84b46..2079667f 100644 --- a/packages/electron-app/electron/main/process-manager.ts +++ b/packages/electron-app/electron/main/process-manager.ts @@ -381,6 +381,9 @@ export class CliProcessManager extends EventEmitter { if (options.dev) { // Dev: run plain HTTP + Vite dev server proxy. args.push("--https", "false", "--http", "true") + // Avoid collisions with an already-running server (and dual-stack ::/0.0.0.0 quirks) + // by forcing an ephemeral port in dev. + args.push("--http-port", "0") } else { // Prod desktop: always keep loopback HTTP enabled. args.push("--https", "true", "--http", "true") diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index e29313de..38b07d80 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -423,7 +423,11 @@ async function main() { const localProtocol: "http" | "https" = httpStart ? "http" : "https" const remoteProtocol: "http" | "https" = httpsStart ? "https" : "http" - const localUrl = `${localProtocol}://localhost:${localStart.port}` + // Use an explicit IPv4 loopback address for the "local" URL. + // On macOS, `localhost` often resolves to ::1 first, and it is possible to have + // another instance bound on IPv6 while this instance binds IPv4 (or vice versa), + // which can lead clients to talk to the wrong process. + const localUrl = `${localProtocol}://127.0.0.1:${localStart.port}` let remoteUrl: string | undefined if (remoteStart) { const wantsAll = options.host === "0.0.0.0" || !isLoopbackHost(options.host)