wailsapp/wails · error

RadioGroup.MenuID: item not found:

Error message

RadioGroup.MenuID: item not found:

What it means

RadioGroup.MenuID panics when asked for the Win32 command ID of a radio menu item that is not a member of that radio group. It is called from Win32Menu.updateRadioGroup (popupmenu_windows.go:425), which runs whenever a checked radio item is updated (UpdateMenuItem / SetChecked). The panic signals an internal bookkeeping mismatch: the item was checked before the popup menu registered its radio groups during build, or the group map was rebuilt while the item reference went stale.

Source

Thrown at v3/pkg/application/popupmenu_windows.go:43

func (r *RadioGroup) Add(id int, item *MenuItem) {
	*r = append(*r, &RadioGroupMember{
		ID:       id,
		MenuItem: item,
	})
}

func (r *RadioGroup) Bounds() (int, int) {
	p := *r
	return p[0].ID, p[len(p)-1].ID
}

func (r *RadioGroup) MenuID(item *MenuItem) int {
	for _, member := range *r {
		if member.MenuItem == item {
			return member.ID
		}
	}
	panic("RadioGroup.MenuID: item not found:")
}

type Win32Menu struct {
	isPopup       bool
	menu          w32.HMENU
	parentWindow  *windowsWebviewWindow
	parent        w32.HWND
	menuMapping   map[int]*MenuItem
	checkboxItems map[*MenuItem][]int
	radioGroups   map[*MenuItem][]*RadioGroup
	menuData      *Menu
	currentMenuID int
	onMenuClose   func()
	onMenuOpen    func()
	isShowing     atomic.Bool // guards against concurrent TrackPopupMenuEx calls

	// bitmaps tracks HBITMAP handles allocated by SetMenuIcons during
	// buildMenu so they can be released when the menu is rebuilt or

View on GitHub (pinned to 0e754b1b40)

Solutions

  1. Only SetChecked(true) on radio items after the popup/context menu has been opened at least once, or drive checked state via NewMenuItemRadio(label, true) at construction
  2. Re-create menu items rather than reusing stale pointers across menu rebuilds
  3. If it reproduces with fresh pointers and a shown menu, file a bug at wailsapp/wails with a minimal repro (popup menu + radio group)

Example fix

// before (panics if menu not yet shown / stale pointer)
myRadioItem.SetChecked(true)

// after (set initial state at construction, update only after menu is shown)
myRadioItem := application.NewMenuItemRadio("Option A", true)
// later, after the context menu has been displayed:
myRadioItem.SetChecked(true)
Defensive patterns

Strategy: try-catch

Try / catch

func safeSetChecked(item *application.MenuItem, checked bool) {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("SetChecked on radio item before menu registration: %v", r)
		}
	}()
	item.SetChecked(checked)
}

Prevention

When it happens

Trigger: On Windows, calling MenuItem.SetChecked(true) on a NewMenuItemRadio item before the containing popup/context menu has been shown (radioGroups not yet populated), or after the menu was rebuilt so the *MenuItem in the map no longer equals the item being updated (pointer identity comparison fails).

Common situations: Toggling radio items from bindings right after startup; updating radio state from a background goroutine while the context menu is being rebuilt; reusing MenuItem pointers across menu rebuilds.

Related errors


AI-assisted analysis of wailsapp/wails@0e754b1b40 (2026-08-15). Data as JSON: /api/errors/9dbed99aa99cdda3. Report an issue: GitHub.