Hive Hive
Sign in

bug: macOS menu bar app shows two status items; the older one is stuck on an endless spinner

GitHub issue · Open

Metadata
Source
tuist/tuist #12680
Updated
Aug 27, 2026
Details

What happened?

Every time I launch the Tuist macOS menu bar app, two Tuist icons appear in the menu bar. The left one works normally. The right one is dead weight: clicking it opens a panel with a spinner (ProgressView) that never resolves. Quitting and reopening the app reproduces it every time — always two icons.

Both icons belong to the same single Tuist process (verified with ps — one PID, no login item, single app bundle), so this is not a duplicate-install problem.

Expected: one menu bar icon.

Root cause analysis

In app/Sources/TuistApp/TuistApp.swift, the FluidMenuBarExtra is created inside the SwiftUI Scene body:

var body: some Scene {
appDelegate.menuBarExtra = FluidMenuBarExtra(title: "Tuist", image: "MenuBarIcon") {
if bootstrapper.isReady {
... MenuBarView(...) ...
} else {
ProgressView()
}
}
return Settings { EmptyView() }
...
}

Three things combine here:

  1. FluidMenuBarExtra.init creates an NSStatusItem immediately, and its docs say it should be “initialized once during the lifecycle of your app”. Creating it in body violates that, because body is re-evaluated.
  2. body is re-evaluated shortly after launch: AppBootstrapper.init runs an async task that flips the @Published isReady, so:
    • Evaluation #1 (at launch, isReady == false) → FluidMenuBarExtra #1.
    • Evaluation #2 (bootstrap done, isReady == true) → FluidMenuBarExtra #2. The first instance isn’t reliably torn down, so its status item stays in the menu bar. (New status items are inserted to the left, which is why the right icon is always the broken one.)
  3. The stuck spinner is explained by FluidMenuBarExtraWindow: it evaluates the content closure once at init (setContentSize(hostingView.intrinsicContentSize) forces the lazy NSHostingView). The if bootstrapper.isReady branch is taken outside of any observing View.body, so instance #1’s window is a frozen snapshot of the ProgressView branch and never re-renders when isReady flips.

A fix would be to create the FluidMenuBarExtra only once, and move the isReady branch into a small wrapper View that holds the bootstrapper as @ObservedObject, so the single window transitions from spinner → MenuBarView reactively. Happy to send a PR for this.

How do we reproduce it?

  1. Install the Tuist macOS app 0.25.7 and launch it.
  2. Watch the menu bar: two Tuist icons appear (the second appears almost immediately after the first, when bootstrap completes).
  3. Click the right icon → panel with a spinner that never finishes. Click the left icon → normal menu.

Error log

No error is logged. The app's session log shows a normal startup
(authentication state initialized, token valid); the duplicate
status item produces no log output.

macOS version

15.6.1

Tuist version

Tuist.app 0.25.7

Xcode version

26.0.1

Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments

No GitHub comments yet.