wailsapp/wails · error

Unknown menu type for newMenuItemImpl: %v

Error message

Unknown menu type for newMenuItemImpl: %v

What it means

newMenuItemImpl (v3/pkg/application/menuitem_linux.go:94) is the GTK4 Linux factory that turns a generic MenuItem into a native linuxMenuItem. It only handles itemType text and submenu; the menuItemType enum (menuitem.go:11-16) also defines separator, checkbox and radio, and any other value falls into default: and panics. checkbox and radio items are routed to their own factories (newCheckMenuItemImpl / newRadioItemImpl) by menu_linux.go's processMenu switch, so in practice this panic means a MenuItem whose itemType is separator (or an out-of-range value) reached the generic text/submenu factory.

Source

Thrown at v3/pkg/application/menuitem_linux.go:94

	if accelerator == nil || l.menuItem == nil {
		return
	}
	InvokeSync(func() {
		setMenuItemAccelerator(l.menuItem.id, accelerator)
	})
}

func newMenuItemImpl(item *MenuItem) *linuxMenuItem {
	result := &linuxMenuItem{
		menuItem: item,
	}
	switch item.itemType {
	case text:
		result.native = menuItemNewWithId(item.label, item.bitmap, item.id)
	case submenu:
		result.native = menuItemNewWithId(item.label, item.bitmap, item.id)
	default:
		panic(fmt.Sprintf("Unknown menu type for newMenuItemImpl: %v", item.itemType))
	}
	if item.accelerator != nil {
		result.setAccelerator(item.accelerator)
	}
	result.setDisabled(result.menuItem.disabled)
	return result
}

func newCheckMenuItemImpl(item *MenuItem) *linuxMenuItem {
	result := &linuxMenuItem{
		menuItem: item,
		native:   menuCheckItemNewWithId(item.label, item.bitmap, item.id, item.checked),
	}
	if item.accelerator != nil {
		result.setAccelerator(item.accelerator)
	}
	result.setDisabled(result.menuItem.disabled)
	return result

View on GitHub (pinned to 0e754b1b40)

Solutions

  1. In processMenu (menu_linux.go), make sure separator items are handled by their own case before any call to newMenuItemImpl; on GTK4, map separators to menuNewSection or skip them like menuitem_linux_gtk3.go does.
  2. If you control the factory, extend newMenuItemImpl's switch with a separator case (e.g. result.native = menuItemNewSeparator()) instead of letting it hit default.
  3. Audit menu construction code for items created without going through NewMenuItem/NewSubMenuItem/NewCheckMenuItem/NewRadioMenuItem/NewSeparator — hand-rolled MenuItem{} literals leave itemType unset (text=0 is fine, but any drift breaks).
  4. Reproduce with GOFLAGS left at default (GTK4 build) and, if the menu works only with -tags gtk3, file a Wails issue noting the GTK4 gap.

Example fix

// before (menuitem_linux.go)
func newMenuItemImpl(item *MenuItem) *linuxMenuItem {
	switch item.itemType {
	case text:
		result.native = menuItemNewWithId(item.label, item.bitmap, item.id)
	case submenu:
		result.native = menuItemNewWithId(item.label, item.bitmap, item.id)
	default:
		panic(fmt.Sprintf("Unknown menu type for newMenuItemImpl: %v", item.itemType))
	}
	...
}

// after
func newMenuItemImpl(item *MenuItem) *linuxMenuItem {
	switch item.itemType {
	case text, submenu:
		result.native = menuItemNewWithId(item.label, item.bitmap, item.id)
	case separator:
		result.native = menuItemNewSeparator()
	default:
		panic(fmt.Sprintf("Unknown menu type for newMenuItemImpl: %v", item.itemType))
	}
	...
}
Defensive patterns

Strategy: validation

Validate before calling

// Before attaching a menu on GTK4 Linux, check every item's type
// is one the factories support via the generic path.
func menuItemsSupportedGTK4(m *app.Menu) bool {
	supported := true
	m.Visit(func(item *app.MenuItem) { // or walk your own item slice
		// text and submenu are safe; checkbox/radio route to their
		// own factories; separator is the risky one on GTK4
	})
	return supported
}

// Simplest guard: only add separators when sections are not used.
if runtime.GOOS == "linux" {
	// omit AddSeparator() or verify your Wails version supports it
}

Type guard

// Narrowing helper for the menuItemType values defined in menuitem.go
type menuItemKind int

func kindOf(itemType menuItemKind) string {
	switch itemType {
	case 0: return "text"
	case 1: return "separator"
	case 2: return "checkbox"
	case 3: return "radio"
	case 4: return "submenu"
	default: return "unknown"
	}
}

Try / catch

// Go: guard menu construction so a factory panic doesn't kill the app
defer func() {
	if r := recover(); r != nil {
		log.Printf("menu build failed: %v", r)
	}
}()
menu := app.NewMenu()

Prevention

When it happens

Trigger: Adding a Menu.AddSeparator() item on Linux GTK4 and having it dispatched as a normal item (separator is skipped in processMenu only when the menu uses sections, so non-section menus with separators can hit it); constructing a MenuItem with a zero or corrupted itemType; new code paths that call newMenuItemImpl directly without first switching on item.itemType; radio/checkbox items misrouted through the generic factory.

Common situations: Building the same Menu tree for Windows/macOS and Linux where separators work on the former; GTK4 port work (v3's default Linux stack since the GTK4 default-flip) where separator support is incomplete; copying menu construction from GTK3-tagged code into the GTK4 path; unit tests that exercise menu item factories directly.

Related errors


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