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(event, listener, record) when typeof listener !== 'function'. The once method wraps the listener to auto-remove after first fire and forwards it to the background page's event recording; a non-function listener cannot be invoked or recorded, so it is rejected upfront.

Source

Thrown at src/resources/api_nw_window.js:178

      privates(this).menu = null;
      this.onClose = bindingUtil.createCustomEvent("nw.Window.onClose", true, false);
      nwNatives.callInWindow(bgPage, "__nw_initwindow", currentRoutingID, this);
    };
    forEach(currentNWWindowInternal, function(key, value) {
      if (!key.endsWith('Internal'))
        NWWindow.prototype[key] = value;
    });

    NWWindow.prototype.onNewWinPolicy      = bindingUtil.createCustomEvent('nw.Window.onNewWinPolicy', false, false);
    NWWindow.prototype.onNavigation        = bindingUtil.createCustomEvent('nw.Window.onNavigation', false, false);
    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.once = function (event, listener, record) {
      if (typeof listener !== 'function')
        throw new TypeError('listener must be a function');
      var fired = false;
      var self = this;

      if (typeof record === 'undefined') {
        nwNatives.callInWindow(bgPage, "__nw_record_event", this, event, listener, true);
      }

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

View on GitHub (pinned to e15da848e9)

Solutions

  1. Guard the call: if (cb) win.once('loaded', cb);.
  2. Pass a real function reference, not a name string.
  3. Resolve handler names from a registry before calling once().

Example fix

// before
win.once('loaded', maybeCallback); // maybeCallback may be undefined
// after
if (typeof maybeCallback === 'function') win.once('loaded', maybeCallback);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof listener !== 'function') {
  throw new TypeError('win.once listener must be a function');
}
win.once(event, listener);

Type guard

function isListenerFn(fn) { return typeof fn === 'function'; }

Prevention

When it happens

Trigger: win.once('loaded', null); win.once('close', undefined); win.once('focus', 'onFocus'); passing a config object instead of a callback.

Common situations: Optional callback patterns where the caller passes a possibly-undefined value without guarding; refactoring from on() to once() and forgetting to ensure the callback; JSON-loaded handler names.

Related errors


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