nwjs/nw.js · error · TypeError

'click' must be a valid Function

Error message

'click' must be a valid Function

What it means

When the `click` property is present on a normal/checkbox MenuItem option, the constructor requires it to be a function (typeof === 'function'). Anything else — a string, an async non-function, or an object — throws a TypeError. The callback is later invoked with no arguments from handleEvent when a 'click' event arrives.

Source

Thrown at src/api/menuitem/menuitem.js:71

    if (option.hasOwnProperty('tooltip'))
      option.tooltip = String(option.tooltip);

    if (option.hasOwnProperty('enabled'))
      option.enabled = Boolean(option.enabled);

    if (option.hasOwnProperty('submenu')) {
      if (v8_util.getConstructorName(option.submenu) != 'Menu')
        throw new TypeError("'submenu' must be a valid Menu");

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

    if (option.hasOwnProperty('click')) {
      if (typeof option.click != 'function')
        throw new TypeError("'click' must be a valid Function");
      else
        this.click = option.click;
    }
  } else if (option.type == 'separator') {
    option = {
      type: 'separator'
    };
  }

  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('tooltip'))
    option.tooltip = '';
  if (!option.hasOwnProperty('enabled'))

View on GitHub (pinned to e15da848e9)

Solutions

  1. Pass the actual function reference: `new nw.MenuItem({ label:'x', click: onOpen })`.
  2. If you have a method name string, resolve it first: `click: obj[methodName].bind(obj)`.
  3. Avoid deep-cloning the options object after attaching the callback.

Example fix

// before
new nw.MenuItem({ label: 'Open', click: 'onOpen' }); // throws

// after
new nw.MenuItem({ label: 'Open', click: onOpen });
Defensive patterns

Strategy: type-guard

Validate before calling

function buildItem(opt) {
  if (opt.hasOwnProperty('click') && typeof opt.click !== 'function')
    throw new TypeError('click must be a function');
  return new nw.MenuItem(opt);
}

Type guard

function isFunction(v) { return typeof v === 'function'; }

Try / catch

try { return new nw.MenuItem(opt); }
catch (e) {
  if (e instanceof TypeError && /click.*Function/.test(e.message)) {
    if (typeof opt.click === 'string' && typeof window[opt.click] === 'function')
      opt.click = window[opt.click];
    else delete opt.click;
    return new nw.MenuItem(opt);
  } throw e;
}

Prevention

When it happens

Trigger: Calling new nw.MenuItem({ label:'x', click: 'onOpen' }) (string name instead of reference), { click: { handleEvent: fn } } (object), or { click: 42 }.

Common situations: Passing a method name as a string (a pattern from other frameworks). Stripping the function during serialization/cloning of options. Async transforms that lose the function type.

Related errors


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