From 3fa28e26092d3af4fe9d5b7ea365ccf69b3ca7d7 Mon Sep 17 00:00:00 2001 From: taliesin-ai <197580575+taliesin-ai@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:46:09 +1000 Subject: [PATCH] fix(v3/macos): marshal main menu updates to UI thread --- v3/UNRELEASED_CHANGELOG.md | 1 + v3/pkg/application/application_darwin.go | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/v3/UNRELEASED_CHANGELOG.md b/v3/UNRELEASED_CHANGELOG.md index 7fd94333899..d996434af59 100644 --- a/v3/UNRELEASED_CHANGELOG.md +++ b/v3/UNRELEASED_CHANGELOG.md @@ -23,6 +23,7 @@ After processing, the content will be moved to the main changelog and this file ## Fixed +- Prevent macOS applications from crashing when replacing the application menu from a Wails callback - macOS app activation now respects activation policy for regular apps only in [PR](https://github.com/wailsapp/wails/pull/5897) by @julianstorer - Guard uninitialized GTK windows in Linux builds in [PR](https://github.com/wailsapp/wails/pull/5898) by @julianstorer - Set explicit opaque background color for Linux WebKit windows before URL load in [PR](https://github.com/wailsapp/wails/pull/5899) by @julianstorer diff --git a/v3/pkg/application/application_darwin.go b/v3/pkg/application/application_darwin.go index ece33619e26..b9f6f361f8b 100644 --- a/v3/pkg/application/application_darwin.go +++ b/v3/pkg/application/application_darwin.go @@ -141,7 +141,19 @@ static void destroyApp(void) { // Set the application menu static void setApplicationMenu(void *menu) { NSMenu *nsMenu = (__bridge NSMenu *)menu; - [NSApp setMainMenu:menu]; + void (^apply)(void) = ^{ + [NSApp setMainMenu:nsMenu]; + }; + + // AppKit requires the main menu to be replaced on the main thread. Menu.Set + // may be called from Wails event listeners, which execute on worker + // goroutines, so marshal the update synchronously. Avoid dispatch_sync when + // already on the main thread because that would deadlock. + if ([NSThread isMainThread]) { + apply(); + } else { + dispatch_sync(dispatch_get_main_queue(), apply); + } } // Get the application name