[client] Fix tray notification crash on Linux

The Wails notifications service connects to the D-Bus session bus in its
ServiceStartup, which Wails runs synchronously inside app.Run. daemonFeed.Watch
was started before app.Run, so the first daemon SubscribeEvents message (which
replays the cached available-update state) fanned out to the tray's update-state
listener and fired an OS notification before that startup ran. The notifier's
*dbus.Conn was still nil, so SendNotification nil-dereferenced deep in godbus and
the panic was fatal to the whole process (observed on Linux Mint).

Move daemonFeed.Watch into the ApplicationStarted hook so it runs after the
service-startup loop, and route every notification send through a new
safeSendNotification helper that recovers from a panic and logs it, so a broken
or unavailable notification bus degrades to a skipped toast instead of crashing.
This commit is contained in:
Zoltán Papp
2026-06-10 16:49:12 +02:00
parent 439b85c584
commit 8a4e686098
6 changed files with 63 additions and 12 deletions
+14 -1
View File
@@ -190,7 +190,20 @@ func main() {
})
listenForShowSignal(context.Background(), tray)
daemonFeed.Watch(context.Background())
// Start the daemon event feed only after Wails has run every service's
// ServiceStartup. The very first daemon SubscribeEvents message replays
// the cached state (status + available update) synchronously, which fans
// out through app.Event into the tray's update-state listener and fires an
// OS notification. If Watch ran before app.Run, that send could beat the
// notifications service's ServiceStartup — on Linux the Wails notifier
// connects to the session bus there, so its *dbus.Conn would still be nil
// and SendNotification would nil-deref (fatal panic on the event-dispatch
// goroutine; observed on Linux Mint). ApplicationStarted fires inside
// app.Run after the synchronous service-startup loop, so the bus is up by
// the time the first event lands.
app.Event.OnApplicationEvent(events.Common.ApplicationStarted, func(*application.ApplicationEvent) {
daemonFeed.Watch(context.Background())
})
if err := app.Run(); err != nil {
log.Fatal(err)