{"record":{"id":"ec1fe9f4ea12b352","repo":"videojs/video.js","slug":"invalid-listener-for-objname-obj-fnname-mu","errorCode":null,"errorMessage":"Invalid listener for ${objName(obj)}#${fnName}; must be a function.","messagePattern":"Invalid listener for (.+?)#(.+?); must be a function\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/js/mixins/evented.js","lineNumber":143,"sourceCode":"/**\n * Validates a value to determine if it is a valid listener. Throws if not.\n *\n * @private\n * @throws {Error}\n *         If the listener is not a function.\n *\n * @param  {Function} listener\n *         The listener to test.\n *\n * @param  {Object} obj\n *         The evented object we are validating for\n *\n * @param  {string} fnName\n *         The name of the evented mixin function that called this.\n */\nconst validateListener = (listener, obj, fnName) => {\n  if (typeof listener !== 'function') {\n    throw new Error(`Invalid listener for ${objName(obj)}#${fnName}; must be a function.`);\n  }\n};\n\n/**\n * Takes an array of arguments given to `on()` or `one()`, validates them, and\n * normalizes them into an object.\n *\n * @private\n * @param  {Object} self\n *         The evented object on which `on()` or `one()` was called. This\n *         object will be bound as the `this` value for the listener.\n *\n * @param  {Array} args\n *         An array of arguments passed to `on()` or `one()`.\n *\n * @param  {string} fnName\n *         The name of the evented mixin function that called this.\n *","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/videojs/video.js/blob/c3a7e0e6d20cf5400c9afbee29965fe4dff393c2/src/js/mixins/evented.js#L125-L161","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nplayer.on('play', this.handlePlay); // handlePlay undefined\n// after\nplayer.on('play', (e) => this.handlePlay(e));","handlingStrategy":"type-guard","validationCode":"function bindListener(fn, ctx) {\n  if (typeof fn !== 'function') throw new TypeError('listener must be a function');\n  return arguments.length > 1 ? fn.bind(ctx) : fn;\n}","typeGuard":"const isListener = (v) => typeof v === 'function';","tryCatchPattern":null,"preventionTips":["Bind methods before passing them: this.handlePlay.bind(this).","Use arrow functions to preserve this.","Verify imported handlers exist (no undefined exports)."],"tags":["events","evented","validation","function-binding"],"backgroundTag":null,"analyzedSha":"c3a7e0e6d20cf5400c9afbee29965fe4dff393c2","analyzedAt":"2026-08-13T04:20:15.471Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}