nwjs/nw.js · error · TypeError

listener must be a function

Error message

listener must be a function

What it means

Thrown by NWWindow.prototype.once when the listener argument is not a function. `once` is a custom one-shot wrapper around on()/removeListener() defined directly on NWWindow, so it validates the listener itself before registering.

Source

Thrown at src/resources/api_nw_newwin.js:134

NWWindow.prototype.LoadingStateChanged = bindingUtil.createCustomEvent("nw.Window.LoadingStateChanged", false, false);
NWWindow.prototype.onDocumentStart     = bindingUtil.createCustomEvent("nw.Window.onDocumentStart",     false, false);
NWWindow.prototype.onDocumentEnd       = bindingUtil.createCustomEvent("nw.Window.onDocumentEnd",       false, false);
NWWindow.prototype.onZoom              = bindingUtil.createCustomEvent("nw.Window.onZoom",              false, false);
NWWindow.prototype.onClose             = bindingUtil.createCustomEvent("nw.Window.onClose", true, false);
NWWindow.prototype.onMinimized         = bindingUtil.createCustomEvent("nw.Window.onMinimized", false, false);
NWWindow.prototype.onMaximized         = bindingUtil.createCustomEvent("nw.Window.onMaximized", false, false);
NWWindow.prototype.onFullscreen        = bindingUtil.createCustomEvent("nw.Window.onFullscreen", false, false);
NWWindow.prototype.onResized           = bindingUtil.createCustomEvent("nw.Window.onResized", false, false);
NWWindow.prototype.onRestore           = bindingUtil.createCustomEvent("nw.Window.onRestore", false, false);

NWWindow.prototype.close = function (force) {
  //console.log(`---> NWWindow.close: ${force} ${this.cWindow.id}`);
  currentNWWindowInternal.close(force, this.cWindow.id);
}

NWWindow.prototype.once = function (event, listener, record) {
  if (typeof listener !== 'function')
    throw new TypeError('listener must be a function');
  var fired = false;
  var self = this;

  function g() {
    self.removeListener(event, g);
    if (!fired) {
      fired = true;
      listener.apply(self, arguments);
    }
  }
  this.on(event, g, false);
  return this;
};

NWWindow.prototype.on = function (event, callback, record) {
  var self = this;

  // Wrap callback to bind to `self`.

View on GitHub (pinned to e15da848e9)

Solutions

  1. Pass a function: `win.once('closed', () => {})`.
  2. Guard: `if (typeof cb === 'function') win.once(ev, cb);`.
  3. Prefer win.on() if you actually want repeated notifications.

Example fix

// before
win.once('closed', handlers.onClose);
// after
if (typeof handlers.onClose === 'function') win.once('closed', handlers.onClose);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof listener !== 'function') throw new TypeError('listener must be a function');

Type guard

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

Try / catch

try { win.once(ev, cb); } catch (e) { if (/listener/.test(e.message)) console.warn('non-function listener skipped'); else throw e; }

Prevention

When it happens

Trigger: Calling `win.once('closed', undefined)`, `win.once('maximized', 'handler')`, or passing a null reference.

Common situations: Handler looked up dynamically resolved to undefined; passing a config value instead of a function.

Related errors


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