nwjs/nw.js · error · Error

Menu.items is immutable

Error message

Menu.items is immutable

What it means

Thrown by the Menu 'items' setter whenever a value is assigned to menu.items. The items collection is backed by privates(this).items and is only mutated through append/insert/remove; direct replacement is forbidden because it would desync the JS array from the native menu.

Source

Thrown at src/resources/api_nw_menu.js:33

  var id = nw.Obj.allocateId();
  option.generatedId = id;

  this.id = id;
  this.type = option.type;
  privates(this).items = [];
  privates(this).option = option;

  var items = privates(this).items;
  nw.Obj.create(id, 'Menu', option);
  messagingNatives.BindToGC(this, function() { items.forEach(function(element) { element._destroy(); }); nw.Obj.destroy(id); });
};

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

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

Menu.prototype.append = function(menu_item) {
  privates(this).items.push(menu_item);
  if (!menu_item.native)
    nw.Obj.callObjectMethod(this.id, 'Menu', 'Append', [ menu_item.id ]);
};

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

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

View on GitHub (pinned to e15da848e9)

Solutions

  1. Clear and re-append: remove existing items via remove() then append() each new one.
  2. Create a fresh Menu instance and replace the reference where it is consumed (e.g. tray.menu).

Example fix

// before
menu.items = [new nw.MenuItem({ label: 'a' })];
// after
while (menu.items.length) menu.remove(menu.items[0]);
menu.append(new nw.MenuItem({ label: 'a' }));
Defensive patterns

Strategy: validation

Validate before calling

// never assign to menu.items; mutate via append/insert/remove only

Type guard

function isMenuItemArray(v) { return Array.isArray(v) && v.every(i => i && i.constructor && i.constructor.name === 'MenuItem'); }

Try / catch

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

Prevention

When it happens

Trigger: Running `menu.items = [item1, item2]` or `menu.items = newArray` to rebuild the menu.

Common situations: Attempting to reset a menu by reassigning items as you would an ordinary array.

Related errors


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