{"record":{"id":"60275df752ff7de8","repo":"nwjs/nw.js","slug":"click-must-be-a-valid-function","errorCode":null,"errorMessage":"'click' must be a valid Function","messagePattern":"'click' must be a valid Function","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/api/menuitem/menuitem.js","lineNumber":71,"sourceCode":"\n    if (option.hasOwnProperty('tooltip'))\n      option.tooltip = String(option.tooltip);\n\n    if (option.hasOwnProperty('enabled'))\n      option.enabled = Boolean(option.enabled);\n\n    if (option.hasOwnProperty('submenu')) {\n      if (v8_util.getConstructorName(option.submenu) != 'Menu')\n        throw new TypeError(\"'submenu' must be a valid Menu\");\n\n      // Transfer only object id\n      v8_util.setHiddenValue(this, 'submenu', option.submenu);\n      option.submenu = option.submenu.id;\n    }\n\n    if (option.hasOwnProperty('click')) {\n      if (typeof option.click != 'function')\n        throw new TypeError(\"'click' must be a valid Function\");\n      else\n        this.click = option.click;\n    }\n  } else if (option.type == 'separator') {\n    option = {\n      type: 'separator'\n    };\n  }\n\n  v8_util.setHiddenValue(this, 'option', option);\n  nw.allocateObject(this, option);\n\n  // All properties must be set after initialization.\n  if (!option.hasOwnProperty('icon'))\n    option.shadowIcon = '';\n  if (!option.hasOwnProperty('tooltip'))\n    option.tooltip = '';\n  if (!option.hasOwnProperty('enabled'))","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/nwjs/nw.js/blob/e15da848e9e08e6e467532dae78995c6ad2f55ee/src/api/menuitem/menuitem.js#L53-L89","documentation":"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.","triggerScenarios":"Calling new nw.MenuItem({ label:'x', click: 'onOpen' }) (string name instead of reference), { click: { handleEvent: fn } } (object), or { click: 42 }.","commonSituations":"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.","solutions":["Pass the actual function reference: `new nw.MenuItem({ label:'x', click: onOpen })`.","If you have a method name string, resolve it first: `click: obj[methodName].bind(obj)`.","Avoid deep-cloning the options object after attaching the callback."],"exampleFix":"// before\nnew nw.MenuItem({ label: 'Open', click: 'onOpen' }); // throws\n\n// after\nnew nw.MenuItem({ label: 'Open', click: onOpen });","handlingStrategy":"type-guard","validationCode":"function buildItem(opt) {\n  if (opt.hasOwnProperty('click') && typeof opt.click !== 'function')\n    throw new TypeError('click must be a function');\n  return new nw.MenuItem(opt);\n}","typeGuard":"function isFunction(v) { return typeof v === 'function'; }","tryCatchPattern":"try { return new nw.MenuItem(opt); }\ncatch (e) {\n  if (e instanceof TypeError && /click.*Function/.test(e.message)) {\n    if (typeof opt.click === 'string' && typeof window[opt.click] === 'function')\n      opt.click = window[opt.click];\n    else delete opt.click;\n    return new nw.MenuItem(opt);\n  } throw e;\n}","preventionTips":["Pass function references, not method-name strings.","Bind methods before assigning: `click: obj.handler.bind(obj)`.","Do not clone/serialize options after attaching callbacks."],"tags":["nwjs","menuitem","constructor","callback","type-validation"],"backgroundTag":null,"analyzedSha":"e15da848e9e08e6e467532dae78995c6ad2f55ee","analyzedAt":"2026-08-13T04:15:35.452Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}