[BUG] Desktop: ⌘Q / "Quit Claude" menu bypasses the running-sessions quit guard

Status Fixed / completed
Reported on v2.1.281
Maintainer reply None cached
Activity 0 comments · opened Sep 28, 2026 · closed Oct 2, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

Claude Desktop has a quit guard ("Claude is still working — Claude is working in N sessions. Quitting now will interrupt that work." with Quit anyway / Wait for Claude / Cancel), but it never appears when quitting with ⌘Q (or the app menu / tray "Quit Claude" item). The app quits immediately and running Code sessions are interrupted.

From the bundled app.asar (Desktop 2.9939.2), the menu item bypasses the guard:

  1. App menu: {label:"Quit {appName}", accelerator:"CmdOrCtrl+Q", click:gxr} (the tray "Quit" item also calls gxr)
  2. function gxr(){TA(!1)}
  3. function TA(e=!1,t=[]){EA()||(DA=!0, e&&(SA=t), a.app.quit())} — sets DA=true before quitting
  4. before-quit handler:

``js
if(DA) DA=!1;
else if(!yA && Bcn?.()){ /* "vetoed by before-quit interceptor" */ t.preventDefault(); return }
`
Bcn is the interceptor registered by installQuitSessionGuard (via Vcn). Because DA is already true`, the guard is never consulted.

TA looks intended for app-initiated relaunches (VM reinstall, developer-mode toggle, config migration…), where skipping the guard makes sense. The user-facing Quit menu item reusing it seems unintended — the guard's own "Quit anyway" path uses a separate one-shot flag (aIt / tIt), which suggests the guard expects normal user quits to reach it.

What Should Happen?

⌘Q / "Quit Claude" should go through the quit session guard: if any session is running (turn in progress, background task, /loop, etc.), show the "Claude is still working" dialog instead of quitting immediately.

Error Messages/Logs

~/Library/Logs/Claude/main.log for a ⌘Q while sessions were running — no guard / "vetoed" line, straight to cleanup:

14:42:06 [info] beforeQuit: handler fired, going down
14:42:06 [info] beforeQuit: handler is not yet run, preventing quit and cleaning things up
14:42:06 [info] Starting onQuitCleanup, hiding windows
...
14:42:06 [info] [CCD CycleHealth] unhealthy cycle for local_… (813s, hadFirstResponse=true, reason=app_quit)
14:42:06 [info] [CCD] Stopping 6 active session(s) on quit
14:42:06 [info] [CCD] Session local_… torn down with 2 live background task(s) — settling as stopped (exited)
...
14:42:13 [info] [LocalSessionManager] 2 restored session(s) lost /loop or background work to the previous quit

Across all rotated main*.log files (Aug–Sep 2026), vetoed by before-quit interceptor appears 0 times.

Steps to Reproduce

  1. Open Claude Desktop → Code tab.
  2. Start a session and give it a long-running task (or start a background task / /loop).
  3. While it is working, press ⌘Q.
  4. Observed: app quits immediately, work is interrupted. Expected: "Claude is still working" dialog.

Is this a regression?

I don't know

Claude Code Version

Claude Desktop 2.9939.2 (bundled Claude Code 2.1.281)

Platform

Anthropic API

Operating System

macOS 27.0 (26A428), Apple Silicon

Terminal/Shell

Other — Claude Desktop app (Code tab)

View original on GitHub ↗