{"record":{"id":"d207425755c7e77c","repo":"videojs/video.js","slug":"plugin-must-be-sub-classed-not-directly-instantia","errorCode":null,"errorMessage":"Plugin must be sub-classed; not directly instantiated.","messagePattern":"Plugin must be sub-classed; not directly instantiated\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/js/plugin.js","lineNumber":200,"sourceCode":" * @fires   Player#pluginsetup:$name\n * @listens Player#dispose\n * @throws  {Error}\n *          If attempting to instantiate the base {@link Plugin} class\n *          directly instead of via a sub-class.\n */\nclass Plugin {\n\n  /**\n   * Creates an instance of this class.\n   *\n   * Sub-classes should call `super` to ensure plugins are properly initialized.\n   *\n   * @param {Player} player\n   *        A Video.js player instance.\n   */\n  constructor(player) {\n    if (this.constructor === Plugin) {\n      throw new Error('Plugin must be sub-classed; not directly instantiated.');\n    }\n\n    this.player = player;\n\n    if (!this.log) {\n      this.log = this.player.log.createLogger(this.name);\n    }\n\n    // Make this object evented, but remove the added `trigger` method so we\n    // use the prototype version instead.\n    evented(this);\n    delete this.trigger;\n\n    stateful(this, this.constructor.defaultState);\n    markPluginAsActive(player, this.name);\n\n    // Auto-bind the dispose method so we can use it as a listener and unbind\n    // it later easily.","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/videojs/video.js/blob/c3a7e0e6d20cf5400c9afbee29965fe4dff393c2/src/js/plugin.js#L182-L218","documentation":"The Plugin base class is abstract: it sets up evented state, logging, and version tracking that only make sense on a subclass with a name and per-instance disposal. Direct instantiation (new Plugin(player)) would lack these and break plugin lifecycle hooks, so the constructor refuses.","triggerScenarios":"Calling new videojs.getPlugin('plugin')(player); mistakenly instantiating the base Plugin returned by registerPlugin or getPlugin('plugin').","commonSituations":"Confusing getPlugin('plugin') (the base class) with a named plugin; copy-paste from docs that show Plugin extension without the extends clause; SSR/inspection code that tries to instantiate every plugin.","solutions":["Define a subclass: class MyPlugin extends videojs.getPlugin('plugin') { ... } and register it, then use player.myPlugin().","Never instantiate the base Plugin; use registerPlugin(name, factory) for basic plugins instead.","If you only need a basic plugin, register a plain function via videojs.registerPlugin(name, fn)."],"exampleFix":"// before\nconst Plugin = videojs.getPlugin('plugin');\nnew Plugin(player); // throws\n// after\nclass MyPlugin extends videojs.getPlugin('plugin') {}\nvideojs.registerPlugin('myPlugin', MyPlugin);\nplayer.myPlugin();","handlingStrategy":"type-guard","validationCode":"function instantiatePlugin(Klass, player) {\n  const Base = videojs.getPlugin('plugin');\n  if (Klass === Base) throw new TypeError('instantiate a Plugin subclass, not the base');\n  return new Klass(player);\n}","typeGuard":"const isPluginSubclass = (K) =>\n  typeof K === 'function' && K !== videojs.getPlugin('plugin') &&\n  videojs.getPlugin('plugin').prototype.isPrototypeOf(K.prototype);","tryCatchPattern":null,"preventionTips":["Always extend videojs.getPlugin('plugin').","Register subclasses and invoke via player.myPlugin(), not new.","Use registerPlugin(name, fn) for basic (function) plugins."],"tags":["plugin","instantiation","abstract-class"],"backgroundTag":null,"analyzedSha":"c3a7e0e6d20cf5400c9afbee29965fe4dff393c2","analyzedAt":"2026-08-13T04:20:15.471Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}