videojs/video.js · error · Error
Invalid listener for ${objName(obj)}#${fnName}; must be a fu
Error message
Invalid listener for ${objName(obj)}#${fnName}; must be a function. What it means
The listener passed to on/one/off must be a function. Video.js invokes listeners via the underlying DOM Events system, so a non-callable value would throw at dispatch time in a less helpful way; this guard fails early at registration. The error message includes the host object and method for context.
Source
Thrown at src/js/mixins/evented.js:143
/**
* Validates a value to determine if it is a valid listener. Throws if not.
*
* @private
* @throws {Error}
* If the listener is not a function.
*
* @param {Function} listener
* The listener to test.
*
* @param {Object} obj
* The evented object we are validating for
*
* @param {string} fnName
* The name of the evented mixin function that called this.
*/
const validateListener = (listener, obj, fnName) => {
if (typeof listener !== 'function') {
throw new Error(`Invalid listener for ${objName(obj)}#${fnName}; must be a function.`);
}
};
/**
* Takes an array of arguments given to `on()` or `one()`, validates them, and
* normalizes them into an object.
*
* @private
* @param {Object} self
* The evented object on which `on()` or `one()` was called. This
* object will be bound as the `this` value for the listener.
*
* @param {Array} args
* An array of arguments passed to `on()` or `one()`.
*
* @param {string} fnName
* The name of the evented mixin function that called this.
*View on GitHub (pinned to c3a7e0e6d2)
Solutions
- Pass an actual function reference: obj.on('click', this.handleClick.bind(this)).
- Use an arrow function to preserve this: obj.on('click', (e) => this.handleClick(e)).
- Verify the imported/exported handler exists before binding.
Example fix
// before
player.on('play', this.handlePlay); // handlePlay undefined
// after
player.on('play', (e) => this.handlePlay(e)); Defensive patterns
Strategy: type-guard
Validate before calling
function bindListener(fn, ctx) {
if (typeof fn !== 'function') throw new TypeError('listener must be a function');
return arguments.length > 1 ? fn.bind(ctx) : fn;
} Type guard
const isListener = (v) => typeof v === 'function';
Prevention
- Bind methods before passing them: this.handlePlay.bind(this).
- Use arrow functions to preserve this.
- Verify imported handlers exist (no undefined exports).
When it happens
Trigger: Calling obj.on('click', undefined), obj.on('click', null), obj.on('click', {handleEvent(){}}) (object not function), or obj.on('click', someMethod) where someMethod was not bound/exported.
Common situations: Forgetting to bind a method; passing a property name instead of the function reference; arrow-function vs method binding confusion; importing a named export that does not exist.
Related errors
- Invalid target for ${objName(obj)}#${fnName}; must be a DOM
- Invalid event type for ${objName(obj)}#${fnName}; must be a
- Invalid event type for ${objName(this)}#trigger; must be a n
- The eventBusKey "${eventBusKey}" does not refer to an elemen
- currentDimension only accepts width or height value
AI-assisted analysis of videojs/video.js@c3a7e0e6d2 (2026-08-13).
Data as JSON: /api/errors/ec1fe9f4ea12b352.
Report an issue: GitHub.