Skip to content

fix(v3): prevent Menu.Update() segfault when app is not running - #5291

Merged
leaanthony merged 7 commits into
wailsapp:masterfrom
wucm667:fix/menu-update-segfault
May 2, 2026
Merged

leaanthony merged 7 commits into
wailsapp:masterfrom
wucm667:fix/menu-update-segfault

Conversation

@wucm667

@wucm667 wucm667 commented Apr 30, 2026 •

Copy link
Copy Markdown
Contributor

Description

Fixes a segmentation fault that occurs when Menu.Update() is called before the Wails application enters a running state.

Bug

Calling Menu.Update() before app.Run() causes a panic on macOS due to InvokeSync attempting to dispatch to the main thread before the run loop is initialized:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x60 pc=0x10161ec28]

goroutine 1 [running, locked to thread]:
github.com/wailsapp/wails/v3/pkg/application.(*App).dispatchOnMainThread(...)
github.com/wailsapp/wails/v3/pkg/application.(*macosMenu).update(...)
github.com/wailsapp/wails/v3/pkg/application.(*Menu).Update(...)

On Linux, similar nil pointer dereferences can occur if the native menu hasn't been initialized.

Root Cause

Menu.Update() unconditionally calls m.impl.update(), which on macOS uses InvokeSync to dispatch work to the main thread. If the application hasn't started yet, the main thread dispatcher is not ready, leading to a crash.

Fix

  1. menu.go: Added a guard in Update() to check if globalApplication is running. If not, the method returns silently. This defers menu updates until the application is ready.

  2. menu_linux.go: Added a nil check for m.native and m.menu in the update() method to prevent nil pointer dereferences on Linux.

Both changes allow Update() calls made during initialization to be harmlessly ignored, with the menu being properly updated once the application starts.

Closes #5290.

Summary by CodeRabbit

  • Bug Fixes
    • Improved menu stability by adding state checks to avoid updating or removing context menu entries when the application isn’t initialized, preventing panics.
    • Guarded native menu handling to skip processing when native/menu structures are not ready, reducing crashes or unexpected behavior during startup and shutdown.

@coderabbitai

coderabbitai Bot commented Apr 30, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d0e45959-5715-4d1e-b0f5-dd318c03c845

📥 Commits

Reviewing files that changed from the base of the PR and between 3593618 and 3e9a882.

📒 Files selected for processing (1)
  • v3/pkg/application/menu.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • v3/pkg/application/menu.go

Walkthrough

Adds early-return guards to menu update paths: menu.Update now returns when there is no global application or the app is not running; platform-specific Linux update now returns when native handle or menu is nil before processing.

Changes

Cohort / File(s) Summary
Core menu guard
v3/pkg/application/menu.go
Guard Menu.Update and context-menu methods by returning early if globalApplication is nil; acquire app.runLock and return if app.running is false before proceeding with radio-group handling and lazy m.impl update.
Linux platform guard
v3/pkg/application/menu_linux.go
linuxMenu.update() now returns early when m.native is nil or m.menu is nil, ensuring processMenu(m.menu) is only called when both are valid.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Bug, MacOS, Linux, v3-alpha, size:S

Suggested reviewers

  • leaanthony

Poem

🐰 I hop through code in careful hops,
I tuck in checks and mend the stops.
Nils no longer cause a fright,
Menus wake up safe and right—
A little nibble, everything's light. 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(v3): prevent Menu.Update() segfault when app is not running' accurately and concisely describes the main change - preventing segmentation faults by adding guards to Menu.Update().
Description check ✅ Passed The PR description provides comprehensive context including the bug, root cause, fix details, and issue closure. It follows the template structure with description, fix explanation, and issue reference.
Linked Issues check ✅ Passed The changes fully address the requirements in #5290: guards against nil globalApplication in menu.go, checks m.native and m.menu on Linux in menu_linux.go, and prevents dispatching to main thread before app is running.
Out of Scope Changes check ✅ Passed All code changes are tightly scoped to the stated objectives: nil checks in menu.go and menu_linux.go to prevent segfaults when Menu.Update() is called before application initialization.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.11.4)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
v3/pkg/application/menu.go (1)

64-72: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Move the guard before any menu work, and read running under runLock.

processRadioGroups() and newMenuImpl() still execute before the early return, so the pre-run path can still touch platform state. Also, globalApplication.running is protected by runLock elsewhere, so reading it directly here introduces a race.

Suggested fix
 func (m *Menu) Update() {
+	app := globalApplication
+	if app == nil {
+		return
+	}
+	app.runLock.Lock()
+	running := app.running
+	app.runLock.Unlock()
+	if !running {
+		return
+	}
+
 	m.processRadioGroups()
 	if m.impl == nil {
 		m.impl = newMenuImpl(m)
 	}
-	if globalApplication == nil || !globalApplication.running {
-		return
-	}
 	m.impl.update()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v3/pkg/application/menu.go` around lines 64 - 72, In Menu.Update move the
early-return guard to the top and read globalApplication.running while holding
the application's runLock: first check globalApplication != nil under a runLock
read lock and if not running return immediately, then proceed to call
m.processRadioGroups() and initialize m.impl via newMenuImpl(m) and finally call
m.impl.update(); ensure you acquire/release the runLock (RLock/RUnlock) when
reading running to avoid the race.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@v3/pkg/application/menu.go`:
- Around line 64-72: In Menu.Update move the early-return guard to the top and
read globalApplication.running while holding the application's runLock: first
check globalApplication != nil under a runLock read lock and if not running
return immediately, then proceed to call m.processRadioGroups() and initialize
m.impl via newMenuImpl(m) and finally call m.impl.update(); ensure you
acquire/release the runLock (RLock/RUnlock) when reading running to avoid the
race.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c6b4ee39-f70e-41d8-ae60-ae56e68d54be

📥 Commits

Reviewing files that changed from the base of the PR and between 3880d83 and eac9cb7.

📒 Files selected for processing (2)
  • v3/pkg/application/menu.go
  • v3/pkg/application/menu_linux.go

Moves the nil/running check to the top of Update() before
processRadioGroups() and newMenuImpl() to prevent GTK crash on Linux
when called before app initialization. Also reads running under
runLock to fix a data race.

Suggested-by: CodeRabbit review on PR wailsapp#5291

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
v3/pkg/application/menu.go (1)

21-24: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

ContextMenu.Update/Destroy can still panic when app is nil

Menu.Update() now returns safely pre-run, but Line 23 and Line 27 still unconditionally dereference globalApplication. NewContextMenu() calls Update() immediately, so this remains a pre-init crash path.

Suggested fix
 func (m *ContextMenu) Update() {
 	m.Menu.Update()
-	globalApplication.ContextMenu.Add(m.name, m)
+	if app := globalApplication; app != nil {
+		app.ContextMenu.Add(m.name, m)
+	}
 }

 func (m *ContextMenu) Destroy() {
-	globalApplication.ContextMenu.Remove(m.name)
+	if app := globalApplication; app != nil {
+		app.ContextMenu.Remove(m.name)
+	}
 }

Also applies to: 26-28

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v3/pkg/application/menu.go` around lines 21 - 24, ContextMenu.Update and
ContextMenu.Destroy unconditionally dereference globalApplication (calling
globalApplication.ContextMenu.Add/Remove) and can panic if the app is nil;
modify both methods (ContextMenu.Update and ContextMenu.Destroy) to first check
that globalApplication and globalApplication.ContextMenu are non-nil before
calling Add/Remove, returning early if either is nil so NewContextMenu can
safely call Update pre-init.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@v3/pkg/application/menu.go`:
- Around line 21-24: ContextMenu.Update and ContextMenu.Destroy unconditionally
dereference globalApplication (calling globalApplication.ContextMenu.Add/Remove)
and can panic if the app is nil; modify both methods (ContextMenu.Update and
ContextMenu.Destroy) to first check that globalApplication and
globalApplication.ContextMenu are non-nil before calling Add/Remove, returning
early if either is nil so NewContextMenu can safely call Update pre-init.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 921a0cc1-cd4c-4fa9-8332-e902118a4725

📥 Commits

Reviewing files that changed from the base of the PR and between eac9cb7 and 3593618.

📒 Files selected for processing (1)
  • v3/pkg/application/menu.go

wucm667 and others added 2 commits May 1, 2026 05:50
Prevents panic when NewContextMenu() is called before app initialization,
since Update() is invoked immediately on construction.

Addresses CodeRabbit review on PR wailsapp#5291
@leaanthony

Copy link
Copy Markdown
Member

✅ Triaged by Wails PR Reviewer

This PR has been reviewed and accepted. Test sub-issues have been created for all platforms.

Head Ref OID: 8761ad03d6cf933d4ca5d6b1eacec7e33afae3bd


This comment serves as a signature that this PR has been triaged. Future runs will skip this PR based on the headRefOid.

@leaanthony

Copy link
Copy Markdown
Member

Linux verification — PASSED

Platform: Ubuntu 24.04.4 LTS (Noble Numbat), Webkit2Gtk v2.50.4, Go 1.25.0, amd64
Commit tested: a83861dbf47b646964495aa16eadc6a87b8f47c1
VM: fresh libvirt/KVM clone of golden-ubuntu-wails on lin-node1

Results

Phase Result Detail
Clone + HEAD check ✅ PASS HEAD matches expected commit
Build wails3 ✅ PASS go install ./cmd/wails3 in 63s, Compiler=go1.25.0
Unit tests pkg/application ✅ PASS ok github.com/wailsapp/wails/v3/pkg/application 0.057s
Unit tests all ✅ PASS (1 pre-existing fail) internal/fileexplorer times out in headless VM — unrelated to PR
Nil-guard reproducer ✅ PASS All 3 checks pass (see below)
Smoke test (v3/examples/menu) ✅ PASS Built and ran 8s, no crash, no segfault

Reproducer output

1. Calling Menu.Update() before app start...
   PASS: no crash
2. Calling ContextMenu.Update() before app start...
   PASS: no crash
3. Calling ContextMenu.Destroy() before app start...
   PASS: no crash

ALL NIL-GUARD CHECKS PASSED — PR fix confirmed on Linux

Smoke test output (v3/examples/menu, 8s run)

INF Build Info: Wails=v3.0.0-dev Compiler=go1.25.0 CGO_ENABLED=1 GOOS=linux
INF Platform Info: ID=ubuntu Name=Ubuntu Version=24.04 Branding="24.04.4 LTS (Noble Numbat)" Webkit2Gtk=v2.50.4
(app exited after timeout/graceful)
SMOKE: completed

Pre-existing failure note

FAIL github.com/wailsapp/wails/v3/internal/fileexplorer 101.540s — tests call OpenFileManager() which tries to launch a desktop file manager via xdg-open. In a headless Xvfb environment with no desktop session the process is killed after 10s. This failure is present on master and is not introduced by this PR.

Verdict

Menu.Update(), ContextMenu.Update(), and ContextMenu.Destroy() all return safely when called before app.Run(). The nil-guard fix in menu.go and menu_linux.go works correctly on Linux. The segfault is resolved. ✅

@leaanthony
leaanthony enabled auto-merge (squash) May 2, 2026 11:47
@leaanthony
leaanthony merged commit 97ccfac into wailsapp:master May 2, 2026
28 checks passed
pull Bot pushed a commit to WangNingkai/wails that referenced this pull request May 3, 2026
…ent Menu.Update() segfault when app is not running
@wucm667
wucm667 deleted the fix/menu-update-segfault branch May 3, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[v3] Menu.Update() can segfault before the application is running

2 participants