nwjs/nw.js · error · Error

Menu.items is immutable

Error message

Menu.items is immutable

What it means

Menu defines `items` as a read-only managed array (a hidden value populated by append/insert). Assigning to menu.items triggers a setter that always throws a plain Error. You must mutate the list through append/insert/remove/removeAt, which keep the native side in sync.

Source

Thrown at src/api/menu/menu.js:43

function Menu(option) {
  if (typeof option != 'object')
    option = { type: 'contextmenu' };

  if (option.type != 'contextmenu' && option.type != 'menubar')
    throw new TypeError('Invalid menu type: ' + option.type);

  this.type = option.type;
  v8_util.setHiddenValue(this, 'items', []);
  nw.allocateObject(this, option);
}
require('util').inherits(Menu, exports.Base);

Menu.prototype.__defineGetter__('items', function() {
  return v8_util.getHiddenValue(this, 'items');
});

Menu.prototype.__defineSetter__('items', function(val) {
  throw new Error('Menu.items is immutable');
});

Menu.prototype.append = function(menu_item) {
  if (v8_util.getConstructorName(menu_item) != 'MenuItem')
    throw new TypeError("Menu.append() requires a valid MenuItem");
    
  this.items.push(menu_item);
  nw.callObjectMethod(this, 'Append', [ menu_item.id ]);
};

Menu.prototype.insert = function(menu_item, i) {
  this.items.splice(i, 0, menu_item);
  nw.callObjectMethod(this, 'Insert', [ menu_item.id, i ]);
}

Menu.prototype.remove = function(menu_item) {
  var pos_hint = this.items.indexOf(menu_item);
  nw.callObjectMethod(this, 'Remove', [ menu_item.id, pos_hint ]);

View on GitHub (pinned to e15da848e9)

Solutions

  1. To clear, loop and call menu.remove(item) (or removeAt) for each entry.
  2. To rebuild, remove all existing items then append the new ones.
  3. If you need a fresh set, construct a new nw.Menu rather than reassigning items.

Example fix

// before
menu.items = [newItem1, newItem2]; // throws

// after
while (menu.items.length) menu.remove(menu.items[0]);
[newItem1, newItem2].forEach(function (i) { menu.append(i); });
Defensive patterns

Strategy: validation

Validate before calling

// Never assign menu.items. To clear:
function clearMenu(menu) {
  while (menu.items.length) menu.remove(menu.items[0]);
}

Try / catch

try { menu.items = next; }
catch (e) {
  if (e instanceof Error && /immutable/.test(e.message)) {
    while (menu.items.length) menu.remove(menu.items[0]);
    next.forEach(function (i) { menu.append(i); });
  } else throw e;
}

Prevention

When it happens

Trigger: Writing `menu.items = [item1, item2]`, `menu.items = []` (attempting to clear), or `menu.items.length = 0`. Any assignment to the property hits the throwing setter.

Common situations: Developers try to bulk-replace or clear a menu by reassigning items (idiomatic for normal arrays). Refactoring that treats items as a normal array property. Attempting to reset state on window reactivation.

Related errors


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