{"record":{"id":"f638b4b4278932a3","repo":"ruvnet/ruflo","slug":"maximum-hooks-limit-reached-for-event-event","errorCode":null,"errorMessage":"Maximum hooks limit reached for event ${event}","messagePattern":"Maximum hooks limit reached for event (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugins/src/hooks/index.ts","lineNumber":82,"sourceCode":"      parallelExecution: false,\n      ...config,\n    };\n  }\n\n  /**\n   * Register a hook.\n   */\n  register(hook: HookDefinition, pluginName?: string): () => void {\n    const event = hook.event;\n\n    if (!this.hooks.has(event)) {\n      this.hooks.set(event, []);\n    }\n\n    const entries = this.hooks.get(event)!;\n\n    if (entries.length >= (this.config.maxHooksPerEvent ?? 50)) {\n      throw new Error(`Maximum hooks limit reached for event ${event}`);\n    }\n\n    const entry: HookEntry = {\n      hook,\n      pluginName,\n      registeredAt: new Date(),\n      executionCount: 0,\n      avgExecutionTime: 0,\n    };\n\n    // Insert in priority order (higher priority first)\n    const priority = hook.priority ?? HookPriorityEnum.Normal;\n    const insertIndex = entries.findIndex(e => (e.hook.priority ?? HookPriorityEnum.Normal) < priority);\n\n    if (insertIndex === -1) {\n      entries.push(entry);\n    } else {\n      entries.splice(insertIndex, 0, entry);","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/hooks/index.ts#L64-L100","documentation":"HookManager.register() enforces a hard cap of config.maxHooksPerEvent (default 50) hooks per event name and throws when the next registration would exceed it. The cap protects the dispatch loop from unbounded fan-out; registrations are not queued or silently dropped — the caller must handle the overflow.","triggerScenarios":"Registering the 51st hook for a single event with default config; registering hooks inside a hot path (per request, per task, per component mount) without ever calling the unregister function returned by register(); loading many plugin instances that each add hooks to the same event like 'task:before'.","commonSituations":"Effects/handlers registered on every request or message without cleanup, accumulating leaks until the cap trips on a long-lived daemon or bot; test suites sharing one HookManager across hundreds of registrations; legitimately needing more than 50 hooks per event in a large plugin fleet.","solutions":["Always call the unregister function returned by register() when the owning plugin/component unmounts or disposes","Raise maxHooksPerEvent in the HookManager constructor config when more than 50 legitimate hooks per event are expected","Deduplicate: key registrations by pluginName + event and skip re-registering an identical hook"],"exampleFix":"// before\nclass Worker {\n  constructor(hooks: HookManager) {\n    hooks.register({ event: 'task:before', handler: onTask }); // leaked every construction\n  }\n}\n\n// after\nclass Worker {\n  private unregister: () => void;\n  constructor(hooks: HookManager) {\n    this.unregister = hooks.register({ event: 'task:before', handler: onTask });\n  }\n  dispose(): void { this.unregister(); }\n}\n\n// or, if you truly need more:\nnew HookManager({ maxHooksPerEvent: 200 })","handlingStrategy":"validation","validationCode":"// Track and dispose registrations; raise the cap when legitimate:\nconst unregister = hooks.register({ event: 'task:before', handler: onTask });\n// on teardown:\nunregister();\n\n// when >50 hooks per event are expected up front:\nconst hooks = new HookManager({ maxHooksPerEvent: 250 });","typeGuard":"type HookDefinitionLike = { event: string; handler?: unknown };\nfunction hasHandler(h: HookDefinitionLike): boolean {\n  return typeof h.handler === 'function';\n}","tryCatchPattern":null,"preventionTips":["Always store the unregister function returned by register() and call it on dispose","Never register hooks inside per-request/per-message hot paths","Deduplicate by pluginName+event before re-registering on reconnect/reload"],"tags":["hooks","event-bus","resource-limits","memory-leak"],"backgroundTag":"resource-limit-exceeded","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}