Problem
Two related issues when closing/quitting the NodeSpace app:
Issue 1: Window close button (red X) doesn't work
Clicking the macOS window close button logs this error and fails to close:
[Debug] [AppInit] Tauri window close requested - flushing pending operations...
[Error] Unhandled Promise Rejection: window.destroy not allowed. Permissions associated with this command: core:window:allow-destroy
The window.destroy() call is blocked by Tauri's permission system.
Issue 2: SIGABRT crash on app quit
When quitting the app (Cmd+Q or menu), a SIGABRT crash occurs in the ggml Metal backend during cleanup:
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Termination Reason: Namespace SIGNAL, Code 6, Abort trap: 6
Terminating Process: nodespace-app [14836]
Application Specific Information:
abort() called
Crash Stack Trace (Key Frames)
0 libsystem_kernel.dylib __pthread_kill + 8
1 libsystem_pthread.dylib pthread_kill + 296
2 libsystem_c.dylib abort + 124
3 nodespace-app ggml_abort + 160
4 nodespace-app ggml_metal_rsets_free + 152
5 nodespace-app ggml_metal_device_free + 24
6 nodespace-app std::__1::unique_ptr<ggml_metal_device, ggml_metal_device_deleter>::~unique_ptr
7 libsystem_c.dylib __cxa_finalize_ranges + 480
8 libsystem_c.dylib exit + 44
9 nodespace-app std::sys::pal::unix::os::exit
10 nodespace-app std::process::exit
11 nodespace-app nodespace_app_lib::run::{{closure}}
Root Cause
The crash occurs during the __cxa_finalize_ranges (C++ cleanup) phase when the app exits. The ggml_metal_device destructor calls ggml_metal_rsets_free which then calls ggml_abort().
This appears to be a known issue with llama.cpp's Metal backend where GPU resources aren't properly released before the app terminates, causing an assertion failure during cleanup.
Environment
- macOS 26.2 (25C56)
- Apple Silicon (Mac16,8)
- Metal GPU backend enabled
- Tauri 2.0 + Rust
Acceptance Criteria
Window Close Button
Metal/llama-cpp Crash
Technical Notes
Window Permission Fix
Add to src-tauri/capabilities/default.json:
{
"permissions": [
"core:window:allow-destroy",
// ... existing permissions
]
}
Metal Crash Solutions
-
Explicit cleanup before exit: Call llama.cpp cleanup functions explicitly in Tauri's RunEvent::Exit handler before std::process::exit() is called
-
Graceful shutdown signal: Use a shutdown signal to tell the AI service to release Metal resources before the app exits
-
Avoid static destruction order issues: The crash happens during __cxa_finalize_ranges which suggests static destruction order issues - may need to use explicit cleanup instead of RAII destructors for Metal resources
Related Code
- Window close handler: Check
src/lib/services/app-init.ts for the close request handler
- Tauri capabilities:
src-tauri/capabilities/default.json
- The crash originates from
nodespace_app_lib::run::{{closure}} which handles Tauri events
References
Problem
Two related issues when closing/quitting the NodeSpace app:
Issue 1: Window close button (red X) doesn't work
Clicking the macOS window close button logs this error and fails to close:
The
window.destroy()call is blocked by Tauri's permission system.Issue 2: SIGABRT crash on app quit
When quitting the app (Cmd+Q or menu), a SIGABRT crash occurs in the ggml Metal backend during cleanup:
Crash Stack Trace (Key Frames)
Root Cause
The crash occurs during the
__cxa_finalize_ranges(C++ cleanup) phase when the app exits. Theggml_metal_devicedestructor callsggml_metal_rsets_freewhich then callsggml_abort().This appears to be a known issue with llama.cpp's Metal backend where GPU resources aren't properly released before the app terminates, causing an assertion failure during cleanup.
Environment
Acceptance Criteria
Window Close Button
core:window:allow-destroypermission to Tauri capabilitiesMetal/llama-cpp Crash
ggml_metal_device_freeis called explicitly during Tauri'son_exitorbefore_exiteventTechnical Notes
Window Permission Fix
Add to
src-tauri/capabilities/default.json:{ "permissions": [ "core:window:allow-destroy", // ... existing permissions ] }Metal Crash Solutions
Explicit cleanup before exit: Call llama.cpp cleanup functions explicitly in Tauri's
RunEvent::Exithandler beforestd::process::exit()is calledGraceful shutdown signal: Use a shutdown signal to tell the AI service to release Metal resources before the app exits
Avoid static destruction order issues: The crash happens during
__cxa_finalize_rangeswhich suggests static destruction order issues - may need to use explicit cleanup instead of RAII destructors for Metal resourcesRelated Code
src/lib/services/app-init.tsfor the close request handlersrc-tauri/capabilities/default.jsonnodespace_app_lib::run::{{closure}}which handles Tauri eventsReferences