#11681: feat(plugins): add cancel support to message_received hook
stale
Cluster:
Plugin and Hook Enhancements
## Summary
Enables plugins to block inbound messages by returning `{ cancel: true }` from the `message_received` hook, matching the existing `message_sending` cancel behavior.
## Changes
**3 files changed:**
- `src/plugins/types.ts` - Add `PluginHookMessageReceivedResult` type with `cancel` field
- `src/plugins/hooks.ts` - Collect results from parallel hook execution, return `{ cancel: true }` if any hook cancelled
- `src/auto-reply/reply/dispatch-from-config.ts` - Await hook results and stop processing if message marked for cancel
## Usage
api.on('message_received', async (event, ctx) => {
if (containsThreat(event.content)) {
return { cancel: true }; // Block the message
}
// Allow through
}, { priority: 100 });
## Use Cases
- Security/DLP plugins blocking malicious content
- Content moderation (spam, abuse)
- Rate limiting excessive messages
- Access control
## Behavior
- Hooks run in **parallel** (preserves existing `Promise.all` behavior)
- If **any** hook returns `{ cancel: true }`, message is blocked
- Dispatcher now **awaits** hook results to check cancel flag (was fire-and-forget)
## Breaking Changes
**Latency impact:** Messages now wait for all `message_received` hooks to complete before processing (was fire-and-forget). Adds ~10ms latency per message if hooks are present.
Plugins should be optimized for fast execution (<10ms recommended).
## Backward Compatibility
Existing `message_received` hooks that return `void` continue to work unchanged.
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
Adds a cancellable return type for the `message_received` plugin hook (`{ cancel?: boolean }`), updates the hook runner to execute all `message_received` handlers in parallel and aggregate their results, and changes the config-based dispatcher to await `message_received` hooks and stop further processing when any hook cancels.
This fits into the existing plugin hook system by extending the hook type map in `src/plugins/types.ts` and exposing the new result type via `src/plugins/hooks.ts`, then consuming the cancel result in the inbound message dispatch path.
<h3>Confidence Score: 3/5</h3>
- This PR is close to mergeable but has a cancel-path lifecycle/diagnostics bug that should be fixed first.
- Core typing and hook aggregation look consistent, but early-return on cancel bypasses existing message processing/idle tracking in the dispatcher, which can break diagnostics/session state accounting. There’s also a behavior change (message_received now blocks) that should be made consistent with expectations across the codebase.
- src/auto-reply/reply/dispatch-from-config.ts
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#10539: feat(hooks): add blocking capability to message_received hookFeat/m...
by khalidovicGPT · 2026-02-06
81.9%
#10109: feat(plugins): invoke message_received and message_sent hooks
by nezovskii · 2026-02-06
80.5%
#18004: feat: Add before_message_dispatch hook for blocking inbound messages
by Andy-Haigh · 2026-02-16
79.7%
#9906: feat: wire message_sending hook in outbound delivery
by teempai · 2026-02-05
78.7%
#8084: fix(plugins): wire up message_sending hook in outbound delivery
by lailoo · 2026-02-03
78.2%
#7545: feat(hooks): add message:received hook for pre-turn automation
by wangtian24 · 2026-02-02
77.9%
#11597: feat(hooks): implement message:received hook
by gnufoo · 2026-02-08
77.5%
#19922: feat(hooks): add message:received and message:sent hook events
by NOVA-Openclaw · 2026-02-18
76.9%
#21155: reply: add cancellable before_reset guard for /new and /reset
by LiShengYang-yiyi · 2026-02-19
76.0%
#7580: feat: add message:received internal hook with prompt injection
by rodrigoschott · 2026-02-03
75.2%