sequelize/sequelize · error · Error
Named listener ${listenerName} already exists for hook ${Str
Error message
Named listener ${listenerName} already exists for hook ${String(hookName)} on ${getName(this.#eventTarget)}. What it means
Thrown by HookHandler.addListener when a listenerName is provided and another listener with the same name is already registered for that hook on the same target. Named listeners are deduplicated so re-registration (e.g. in hot-reload or repeated setup) is treated as a programmer error rather than silently stacking duplicates.
Source
Thrown at packages/core/src/hooks.ts:155
*
* Returns a function that can be called to deregister the listener.
*
* @param hookName
* @param listener
* @param listenerName
*/
addListener<HookName extends keyof HookConfig>(
hookName: HookName,
listener: HookConfig[HookName],
listenerName?: string,
): () => void {
this.#assertValidHookName(hookName);
if (listenerName) {
const existingListener = this.#getNamedListener(hookName, listenerName);
if (existingListener) {
throw new Error(
`Named listener ${listenerName} already exists for hook ${String(hookName)} on ${getName(this.#eventTarget)}.`,
);
}
}
this.#listeners.append(hookName, { callback: listener, listenerName });
return () => {
this.removeListener(hookName, listenerName || listener);
};
}
addListeners(listeners: {
[Key in keyof HookConfig]?: AllowArray<
HookConfig[Key] | { name: string | symbol; callback: HookConfig[Key] }
>;
}) {
for (const hookName of this.#validHookNames) {View on GitHub (pinned to 7e1deec499)
Solutions
- Call removeListener(hook, 'myName') (or use the returned deregister function) before re-registering.
- Guard registration with a module-level flag so setup runs once.
- If duplicates are acceptable, omit listenerName and register anonymously.
- In tests, call removeAllListeners() in beforeEach.
Example fix
// before
model.addHook('beforeCreate', auditFn, 'audit');
// re-run on HMR
model.addHook('beforeCreate', auditFn, 'audit'); // throws
// after
const off = model.addHook('beforeCreate', auditFn, 'audit');
if (module.hot) module.hot.dispose(() => off()); Defensive patterns
Strategy: try-catch
Validate before calling
// Check before adding
if (handler.getListenerCount(hookName) > 0 && handler.hasNamed(hookName, 'audit')) { /* skip or remove first */ } Try / catch
try { const off = model.addHook('beforeCreate', fn, 'audit'); } catch (e) { if (/already exists/.test(String(e))) { model.removeHook('beforeCreate', 'audit'); model.addHook('beforeCreate', fn, 'audit'); } else throw e; } Prevention
- Use the returned deregister function on HMR/test teardown
- Guard registration with a module-level flag
- Omit the name to allow anonymous duplicates
- Call removeAllListeners() in test beforeEach
When it happens
Trigger: Calling addListener(hook, fn, 'myName') twice without removing the first; a module's setup function running twice (HMR, repeated test setup); addListeners with a `{ name, callback }` object colliding with an earlier registration.
Common situations: Hot-module reload re-running model registration; test suites that re-import modules without clearing hooks; plugin loaded twice; refactoring that moved addListener into a frequently-called function.
Related errors
- ${listener.listenerName ? `Listener ${listener.listenerName}
- Target ${getName(this.#eventTarget)} does not support a hook
- Model must be initialized before running hooks on it.
- sequelize.close was called, new connections cannot be establ
- Unable to determine to which pool the connection belongs
AI-assisted analysis of sequelize/sequelize@7e1deec499 (2026-08-03).
Data as JSON: /data/errors/3a631d4e98553fd6.json.
Report an issue: GitHub.