nwjs/nw.js · error · TypeError
'menu' must be a valid Menu
Error message
'menu' must be a valid Menu
What it means
Thrown by the Tray constructor when option.hasOwnProperty('menu') is true but option.menu.constructor.name !== 'Menu'. The tray menu must be a real nw.MenuItem collection built via nw.Menu({ type: 'menubar' }) so the constructor can read .id and hand it to the native layer. The check uses constructor.name rather than instanceof, so cross-realm Menu objects whose constructor name matches 'Menu' will pass, but plain objects/arrays will not.
Source
Thrown at src/resources/api_nw_tray.js:64
if (option.hasOwnProperty('iconsAreTemplates'))
option.iconsAreTemplates = Boolean(option.iconsAreTemplates);
else
option.iconsAreTemplates = true;
if (option.hasOwnProperty('tooltip'))
option.tooltip = String(option.tooltip);
if (option.hasOwnProperty('click')) {
if (typeof option.click != 'function') {
throw new TypeError("'click' must be a valid Function");
} else {
this.click = option.click;
}
}
if (option.hasOwnProperty('menu')) {
if (option.menu.constructor.name != 'Menu')
throw new TypeError("'menu' must be a valid Menu");
// Transfer only object id
privates(this).menu = option.menu;
option.menu = option.menu.id;
}
var id = nw.Obj.allocateId();
this.id = id;
privates(this).option = option;
// All properties must be set after initialization.
if (!option.hasOwnProperty('icon'))
option.shadowIcon = '';
if (!option.hasOwnProperty('alticon'))
option.shadowAlticon = '';
if (!option.hasOwnProperty('tooltip'))
option.tooltip = '';
View on GitHub (pinned to e15da848e9)
Solutions
- Construct the menu first: const menu = new nw.Menu({ type: 'menubar' }); menu.append(new nw.MenuItem({ label: 'Item' })); then new nw.Tray({ title: 'x', menu: menu }).
- Ensure bundlers/minifiers preserve function .name (configure keep_fnames) if you hit the cross-bundle case.
Example fix
// before
new nw.Tray({ title: 'x', menu: [{ label: 'Quit' }] });
// after
const menu = new nw.Menu({ type: 'menubar' });
menu.append(new nw.MenuItem({ label: 'Quit' }));
new nw.Tray({ title: 'x', menu: menu }); Defensive patterns
Strategy: type-guard
Validate before calling
if ('menu' in option && !(option.menu instanceof nw.Menu)) {
throw new TypeError('option.menu must be an nw.Menu instance');
}
// note: the source checks constructor.name === 'Menu'; instanceof is stricter and safer Type guard
function isTrayMenuValid(o) { return !o.hasOwnProperty('menu') || (o.menu && o.menu.constructor && o.menu.constructor.name === 'Menu'); } Prevention
- Construct the menu with new nw.Menu({ type: 'menubar' }) before passing it.
- Do not pass arrays of plain objects as the menu.
- Configure bundlers to keep function .name if running in a bundled environment.
When it happens
Trigger: new nw.Tray({ title: 'x', menu: [{ label: 'a' }] }) (array of plain objects); passing a menu config object instead of a constructed nw.Menu; passing a DOM element or other object whose constructor.name is not 'Menu'.
Common situations: Confusing the menu config with the constructed Menu; building items inline instead of via nw.Menu; minified/transformed code where constructor.name is mangled (rare but possible with aggressive bundlers).
Related errors
- 'menu' property requries a valid Menu
- Invalid menu type: {option.type}
- 'menu' must be a valid Menu
- 'menu' property requries a valid Menu
- Invalid option.
AI-assisted analysis of nwjs/nw.js@e15da848e9 (2026-08-13).
Data as JSON: /api/errors/b4dd3665c02fd009.
Report an issue: GitHub.