Preview: switching projects leaks previous project's window and orphans its dev-server process, blocking new project's ports

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 18, 2026

Summary

Switching the Browser pane / preview to a different project does not reuse or cleanly replace the previous project's dev-server process. Instead:

  1. The preview UI can show the previous project's window/content instead of the newly opened project's.
  2. The previous project's dev server (or, in our case, a Firebase emulator suite) is left running and bound to its ports, rather than being reused or torn down. When the new project's preview then tries to start its own dev server/emulator on the same (often default/fixed) ports, it fails with "port already in use," or the two processes end up fighting over the same port.

This looks related to #61580 (preview panel leaking across chats/projects) and #50544 (preview_start not reaping/killing the previous dev-server process before retrying), but neither issue covers the specific "switch projects → orphaned process squats fixed ports → new project's preview can't start" chain, so filing separately with concrete evidence below.

Steps to reproduce

  1. Open project A (in our case, a Firebase Cloud Functions project) in Claude Code and use the Browser-pane preview tooling to run/preview A's dev server or local emulator suite.
  2. Without explicitly stopping that server, switch to a different project directory / session (project B).
  3. Open the preview for project B.
  4. Observed: the preview surfaced project A's window rather than B's, and separately, attempting to start B's own dev server/emulator failed because ports from A's still-running process were not released or reused.

Evidence from this session

We were debugging unrelated "emulator keeps disconnecting" flakiness in a Firebase Cloud Functions project. Running the project's own test suite (which shells out to firebase emulators:exec) failed immediately:

⚠ auth: Port 9099 is not open on localhost (127.0.0.1,::1), could not start Authentication Emulator.
⚠ firestore: Port 8080 is not open on localhost (127.0.0.1,::1), could not start Firestore Emulator.
Error: Could not start Authentication Emulator, port taken.

lsof -i :9099 -i :8080 showed a live node process and a live java process already bound to those exact ports:

node    38387 xtfer   42u  IPv4  ...      0t0  TCP localhost:9099 (LISTEN)
java    38536 xtfer  168u  IPv6  ...      0t0  TCP localhost:http-alt (LISTEN)

Critically, the current session's own preview registry (preview_list) reported zero tracked servers:

[]
Session preview: { "previewId": "preview-local_...", "tabs": [] }

So these two processes were not started by (and are not tracked by) the current session — they are orphaned from an earlier preview/session on a different project, still squatting Firebase's fixed default emulator ports (9099/8080/5001/8085), and blocking the current project's own emulator from starting.

Expected behavior

  • Preview/dev-server sessions should be scoped per project, not leak across projects or chats.
  • Switching projects should either reuse an already-running dev server for that same project, or cleanly stop the previous project's server before/when starting a new one — not leave it orphaned and unreachable from the new session's own process-tracking state.
  • At minimum, preview_list/equivalent tracking should be able to see and clean up processes it previously spawned, even across a project switch, so users aren't left manually hunting PIDs with lsof/kill.

Environment

  • macOS (Darwin 24.6.0)
  • Project: Firebase Cloud Functions (Node) + Flutter, using the Browser-pane preview tooling and firebase emulators:exec for local dev/test
  • Claude Code CLI session, Browser pane / preview MCP tooling

Related issues

  • #61580 — Launch preview panel leaks across chats/projects (UI-level symptom match)
  • #50544 — preview_start doesn't reap/kill previous dev-server process before retry (process-lifecycle symptom match, closed as stale rather than fixed)

View original on GitHub ↗