#9126: fix(macOS): prevent recursive menu nesting in status bar
app: macos
stale
Cluster:
macOS Notification and Menu Fixes
## Summary
- Guards `menuWillOpen`/`menuDidClose` in `MenuSessionsInjector` to only process the main status item menu, not submenus
- Prevents infinite recursive nesting when opening submenus like "Usage cost (30 days)"
Fixes #9019
## Root Cause
The `MenuSessionsInjector` class is set as the delegate for the main status item menu. When a submenu opens (e.g., "Usage cost (30 days)"), `menuWillOpen` is called for that submenu as well. Without checking that the menu is the main status item menu, `inject(into: menu)` was called on the submenu, adding another "Usage cost (30 days)" menu item inside it - creating infinite recursion.
## Fix
Added guard clauses to `menuWillOpen` and `menuDidClose`:
```swift
guard menu === self.statusItem?.menu else { return }
```
This ensures:
- Menu injection only happens for the main status item menu
- State management (`isMenuOpen`, tasks) only tracks the main menu lifecycle
- Submenus are left unchanged
## Test plan
- [x] macOS app compiles successfully (`swift build`)
- [ ] Manual test: Open menu bar → hover "Usage cost (30 days)" → verify no recursive submenu
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This change updates `MenuSessionsInjector` (the `NSMenuDelegate` for the macOS status item menu) to ignore submenu open/close events by guarding `menuWillOpen(_:)` and `menuDidClose(_:)` with `guard menu === self.statusItem?.menu else { return }`. This prevents the injector from re-running `inject(into:)` on submenus (e.g., the “Usage cost (30 days)” submenu, which also sets `menu.delegate = self`), eliminating the infinite recursive nesting of injected menu items while keeping menu open state/tasks scoped to the main status bar menu lifecycle.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk.
- The change is a narrow guard in NSMenuDelegate callbacks that prevents a known infinite-recursion bug by scoping injection/state changes to the main status item menu; it doesn’t alter data formats or external interfaces.
- apps/macos/Sources/Clawdbot/MenuSessionsInjector.swift
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#23135: feat(macos): menubar UI consistency overhaul — sections, hover, ico...
by apethree · 2026-02-22
70.5%
#10182: fix: skip non-openclaw LaunchAgents in doctor gateway scan
by Yida-Dev · 2026-02-06
65.1%
#9926: fix(macos): guard UNUserNotificationCenter when no bundle identifier
by webcpu · 2026-02-05
64.9%
#11208: fix(config): prevent __OPENCLAW_REDACTED__ corruption on config writes
by janckerchen · 2026-02-07
64.4%
#15909: Guard notifications on macOS; fix focus issue and build fixes
by jasonkneen · 2026-02-14
64.4%
#18685: fix(ui): prevent tabnabbing in chat images
by Mariana-Codebase · 2026-02-16
63.7%
#8260: fix(macOS): gateway readiness detection + reversible Configure later
by xksteven · 2026-02-03
63.0%
#3859: fix(macOS): keep Not configured from snapping back to remote (AI-as...
by innoiso · 2026-01-29
62.7%
#3474: fix(macos): menu bar activity badge not showing during agent work
by elektricM · 2026-01-28
62.6%
#12191: fix: guard against undefined model.input in display and scan layers
by mcaxtr · 2026-02-09
62.6%