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 nw.Tray constructor when 'menu' is present but the value's constructor name is not 'Menu'. nw validates the menu via v8_util.getConstructorName, so a plain object or any non-nw.Menu instance is rejected even if it looks menu-like.

Source

Thrown at src/api/tray/tray.js:63

  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 (v8_util.getConstructorName(option.menu) != 'Menu')
      throw new TypeError("'menu' must be a valid Menu");

    // Transfer only object id
    v8_util.setHiddenValue(this, 'menu', option.menu);
    option.menu = option.menu.id;
  }

  v8_util.setHiddenValue(this, 'option', option);
  nw.allocateObject(this, 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 = '';
}
require('util').inherits(Tray, exports.Base);

View on GitHub (pinned to e15da848e9)

Solutions

  1. Build the menu with `new nw.Menu({ type: 'contextmenu', items: [...] })` before passing it.
  2. Ensure the same Menu class (nw.Menu) is used, not a custom subclass with a different constructor name.

Example fix

// before
new nw.Tray({ menu: { items: [{ label: 'x' }] } });
// after
const menu = new nw.Menu();
menu.append(new nw.MenuItem({ label: 'x' }));
new nw.Tray({ menu });
Defensive patterns

Strategy: type-guard

Validate before calling

if (option.hasOwnProperty('menu') && !(option.menu instanceof nw.Menu)) {
  throw new Error('option.menu must be a nw.Menu instance');
}

Type guard

function isMenu(v) { return v && v.constructor && v.constructor.name === 'Menu'; }

Try / catch

try { new nw.Tray(opt); } catch (e) { if (/menu/.test(e.message)) { opt.menu = new nw.Menu(); new nw.Tray(opt); } else throw e; }

Prevention

When it happens

Trigger: Calling `new nw.Tray({ menu: { items: [] } })` or passing a JSON-deserialized object as the menu.

Common situations: Passing a submenu built with a different framework; forgetting to wrap items in `new nw.Menu({ ... })`.

Related errors


AI-assisted analysis of nwjs/nw.js@e15da848e9 (2026-08-13). Data as JSON: /api/errors/011b186ade9bd257. Report an issue: GitHub.