Skip to content

Bug: App hangs on quit and requires force kill (macOS, Show in Menu Bar + Always on Top enabled) #470

Description

@brokosz

Describe the bug
Heynote won't quit via Cmd+Q, dock right-click > Quit, or menu bar quit. The process stays alive and the only way to kill it is kill -9 or Force Quit from Activity Monitor. Both "Show in Menu Bar" and "Always on Top" are enabled.

To Reproduce

  1. Enable "Show in Menu Bar" and "Always on Top" in settings
  2. Use the app normally
  3. Attempt to quit via Cmd+Q (or dock quit, or menu bar quit)
  4. App does not exit. Process remains alive indefinitely and must be force killed.

Expected behavior
App should quit cleanly.

Screenshots
N/A (no visual error, the app just silently refuses to exit)

Desktop (please complete the following information):

  • OS: macOS 26.4 (Tahoe)
  • Version: 2.8.2

Additional context
I traced through the source and found the root cause in the win.on("close") handler in electron/main/index.ts. It's a race condition in the save-before-quit flow:

  1. before-quit sets forceQuit = true
  2. The close handler checks fileLibrary.contentSaved -- if false, it calls event.preventDefault() and sends WINDOW_CLOSE_EVENT to the renderer
  3. The renderer is supposed to call buffer:saveAndQuit, which saves all buffers, sets contentSaved = true, then calls app.quit()
  4. If the renderer never completes that IPC call (e.g. getAllBufferContent() throws, the renderer is frozen, or the webContents are already being destroyed), contentSaved stays false forever
  5. Every subsequent app.quit() triggers close again, which hits preventDefault() again, creating an infinite loop

With "Show in Menu Bar" enabled, there's an additional issue: the first check in the close handler (!forceQuit && CONFIG.get("settings.showInMenu")) can hide the window instead of proceeding to the save logic if event ordering between before-quit and close isn't guaranteed.

There is no timeout or fallback. If the IPC round-trip fails for any reason, the app is permanently stuck.

A possible fix would be adding a timeout so the app force-quits if contentSaved doesn't flip within a few seconds:

if (!!fileLibrary && !fileLibrary.contentSaved) {
    event.preventDefault()
    win?.webContents.send(WINDOW_CLOSE_EVENT)
    setTimeout(() => {
        if (!fileLibrary.contentSaved) {
            fileLibrary.contentSaved = true
            app.quit()
        }
    }, 5000)
}

Related issues:

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions