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
- Enable "Show in Menu Bar" and "Always on Top" in settings
- Use the app normally
- Attempt to quit via Cmd+Q (or dock quit, or menu bar quit)
- 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:
before-quit sets forceQuit = true
- The
close handler checks fileLibrary.contentSaved -- if false, it calls event.preventDefault() and sends WINDOW_CLOSE_EVENT to the renderer
- The renderer is supposed to call
buffer:saveAndQuit, which saves all buffers, sets contentSaved = true, then calls app.quit()
- 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
- 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:
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 -9or Force Quit from Activity Monitor. Both "Show in Menu Bar" and "Always on Top" are enabled.To Reproduce
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):
Additional context
I traced through the source and found the root cause in the
win.on("close")handler inelectron/main/index.ts. It's a race condition in the save-before-quit flow:before-quitsetsforceQuit = trueclosehandler checksfileLibrary.contentSaved-- iffalse, it callsevent.preventDefault()and sendsWINDOW_CLOSE_EVENTto the rendererbuffer:saveAndQuit, which saves all buffers, setscontentSaved = true, then callsapp.quit()getAllBufferContent()throws, the renderer is frozen, or the webContents are already being destroyed),contentSavedstaysfalseforeverapp.quit()triggerscloseagain, which hitspreventDefault()again, creating an infinite loopWith "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 betweenbefore-quitandcloseisn'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
contentSaveddoesn't flip within a few seconds:Related issues:
win.hide()/win.show()logic)