{"record":{"id":"ec95fd7e37c57a48","repo":"ruvnet/ruflo","slug":"hook-for-event-this-event-requires-a-handler","errorCode":null,"errorMessage":"Hook for event ${this.event} requires a handler","messagePattern":"Hook for event (.+?) requires a handler","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugins/src/hooks/index.ts","lineNumber":353,"sourceCode":"  transform(transformer: (data: unknown) => unknown): this {\n    this.transformers.push(transformer);\n    return this;\n  }\n\n  /**\n   * Set the handler function.\n   */\n  handle(handler: HookHandler): this {\n    this.handler = handler;\n    return this;\n  }\n\n  /**\n   * Build the hook definition.\n   */\n  build(): HookDefinition {\n    if (!this.handler) {\n      throw new Error(`Hook for event ${this.event} requires a handler`);\n    }\n\n    const originalHandler = this.handler;\n    const conditions = this.conditions;\n    const transformers = this.transformers;\n\n    // Wrap handler with conditions and transformers\n    const wrappedHandler: HookHandler = async (context: HookContext) => {\n      // Check conditions\n      for (const condition of conditions) {\n        if (!condition(context)) {\n          return { success: true, data: context.data };\n        }\n      }\n\n      // Apply transformers\n      let data = context.data;\n      for (const transformer of transformers) {","sourceCodeStart":335,"sourceCodeEnd":371,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugins/src/hooks/index.ts#L335-L371","documentation":"HookBuilder.build() validates that a handler was set via .handle(fn) and throws otherwise. It is builder validation: the resulting HookDefinition would be unexecutable, so build() refuses to emit it. Conditions and transformers are optional; only the handler is mandatory.","triggerScenarios":"Building a fluent chain that is missing .handle(): builder.on('x').when(cond).build(); conditional wiring where the branch that calls .handle() is skipped by a typo'd if or a falsy check; refactors that renamed the handle() call or moved it after build().","commonSituations":"Copy-pasted builder code with the handle line dropped; dynamically assembled builders whose handler depends on a runtime branch; incomplete first-draft code passing tests of construction but failing at build().","solutions":["Add .handle(fn) to the chain before .build()","When the handler is genuinely optional-by-branch, provide a no-op default handler or only call build() once a handler is assigned","Centralize builder completion in a single factory function that always sets a handler as its last step"],"exampleFix":"// before\nconst hook = builder.on('task:after').when(ctx => ctx.ok).build(); // throws\n\n// after\nconst hook = builder\n  .on('task:after')\n  .when(ctx => ctx.ok)\n  .handle(async ctx => ({ success: true, data: ctx.data }))\n  .build();","handlingStrategy":"validation","validationCode":"// Complete the builder in one factory so the handler can never be missing:\nfunction buildHook(\n  builder: HookBuilder,\n  handler: HookHandler | undefined\n): HookDefinition {\n  if (!handler) {\n    throw new Error('refusing to build hook without a handler');\n  }\n  return builder.handle(handler).build();\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always end the fluent chain with .handle(fn) before .build()","Provide a no-op default handler when wiring is conditional","Let one factory own builder completion instead of scattering chains"],"tags":["builder","hooks","validation","api-misuse"],"backgroundTag":"missing-required-argument","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"}