{"record":{"id":"d409ff4bbf7d3213","repo":"codex-team/editor.js","slug":"eventdispatcher-off-there-is-no-subscribers-fo","errorCode":null,"errorMessage":"EventDispatcher .off(): there is no subscribers for event \"${eventName.toString()}\". Probably, .off() called before .on()","messagePattern":"EventDispatcher \\.off\\(\\): there is no subscribers for event \"(.+?)\"\\. Probably, \\.off\\(\\) called before \\.on\\(\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/components/utils/events.ts","lineNumber":98,"sourceCode":"      return;\n    }\n\n    this.subscribers[eventName].reduce((previousData, currentHandler) => {\n      const newData = currentHandler(previousData);\n\n      return newData !== undefined ? newData : previousData;\n    }, data);\n  }\n\n  /**\n   * Unsubscribe callback from event\n   *\n   * @param eventName - event name\n   * @param callback - event handler\n   */\n  public off<Name extends keyof EventMap>(eventName: Name, callback: Listener<EventMap[Name]>): void {\n    if (this.subscribers[eventName] === undefined) {\n      console.warn(`EventDispatcher .off(): there is no subscribers for event \"${eventName.toString()}\". Probably, .off() called before .on()`);\n\n      return;\n    }\n\n    for (let i = 0; i < this.subscribers[eventName].length; i++) {\n      if (this.subscribers[eventName][i] === callback) {\n        delete this.subscribers[eventName][i];\n        break;\n      }\n    }\n  }\n\n  /**\n   * Destroyer\n   * clears subscribers list\n   */\n  public destroy(): void {\n    this.subscribers = {} as Subscriptions<EventMap>;","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/codex-team/editor.js/blob/5f45dabbe5690b7033b2f9047d3985add5045fdd/src/components/utils/events.ts#L80-L116","documentation":"EventsDispatcher.off(eventName, callback) warns (console.warn, does not throw) when there are no subscribers at all for the event, which almost always means .off() was called before (or without) a matching .on(). The message includes the event name.","triggerScenarios":"Calling off('event', cb) before any on('event', ...); calling off on a different dispatcher instance than the one subscribed; unsubscribing after the component already cleared its subscribers.","commonSituations":"React/Vue cleanup functions running before mount subscription completed; duplicated components sharing state; ordering bugs in destroy() hooks.","solutions":["Ensure every off() has a matching prior on() with the same dispatcher and event name","Subscribe in the same lifecycle that unsubscribes (e.g. onMounted/onUnmounted pairing)","Guard with a check of the subscribers map or a flag before calling off"],"exampleFix":"// before\nonUnmounted(() => dispatcher.off('blockChanged', handler)); // on() may never have run\n// after\nlet subscribed = false;\nonMounted(() => { dispatcher.on('blockChanged', handler); subscribed = true; });\nonUnmounted(() => { if (subscribed) dispatcher.off('blockChanged', handler); });","handlingStrategy":"validation","validationCode":"let subscribed = false; dispatcher.on('evt', handler); subscribed = true; if (subscribed) dispatcher.off('evt', handler);","typeGuard":"const hasSubscribers = (d: EventsDispatcher<any>, evt: string): boolean => Array.isArray((d as any).subscribers?.[evt]);","tryCatchPattern":null,"preventionTips":["Pair on/off in the same lifecycle scope","Subscribe before scheduling cleanup"],"tags":["events","off","lifecycle","console-warn"],"backgroundTag":"event-listener-leak","analyzedSha":"5f45dabbe5690b7033b2f9047d3985add5045fdd","analyzedAt":"2026-08-27T22:50:23.124Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}