openclaw - 💡(How to fix) Fix Discord reply delivery bypasses message_sending plugin hooks [1 participants]

Official PRs (…)
ON THIS PAGE

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#59350Fetched 2026-04-08 02:25:37
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
0
Participants

The Discord channel's reply delivery path (originating from extensions/discord/src/monitor/reply-delivery.ts, compiled into route-resolution-CRUBXA7Y.js) sends messages directly via the Discord REST API without running message_sending plugin hooks. Plugins that use api.on("message_sending", ...) to transform or filter outgoing content have no effect on Discord messages. Telegram and the general delivery path both correctly invoke the hook. Only Discord is affected.

Root Cause

Verified on 2026.4.1 (da64a97). The global hook runner correctly reports:

  • getGlobalHookRunner() returns a valid runner
  • hasHooks("message_sending")true
  • registry.typedHooks.filter(h => h.hookName === "message_sending").length1

The hook IS registered and visible to the runner — it is simply never called by the Discord delivery code path.

Discord's reply delivery in route-resolution-CRUBXA7Y.js calls sendDiscordChunkWithFallback() directly (via REST or webhook), with no hookRunner.runMessageSending() invocation anywhere in that path.

Fix Action

Workaround

Using before_agent_start hook with appendSystemContext to inject a formatting instruction as a soft prevention layer. This works but is not guaranteed since the LLM may still generate tables when the user explicitly requests them.

RAW_BUFFERClick to expand / collapse

Summary

The Discord channel's reply delivery path (originating from extensions/discord/src/monitor/reply-delivery.ts, compiled into route-resolution-CRUBXA7Y.js) sends messages directly via the Discord REST API without running message_sending plugin hooks. Plugins that use api.on("message_sending", ...) to transform or filter outgoing content have no effect on Discord messages. Telegram and the general delivery path both correctly invoke the hook. Only Discord is affected.

Expected behavior

message_sending hooks should fire for all outgoing messages regardless of channel, consistent with:

  • Telegram delivery (delivery-*.js line ~522): calls getGlobalHookRunner()runMessageSending()
  • General delivery path (deliver-*.js line ~835): calls getGlobalHookRunner()runMessageSending()

Reproduction

  1. Create a plugin that hooks message_sending and modifies event.content (e.g., converting markdown tables to bullet lists)
  2. Send a message to the bot via Discord DM
  3. Observe: the hook callback never executes; content is delivered unmodified
  4. Send the same message via Telegram → hook fires, content is modified as expected

Analysis

Verified on 2026.4.1 (da64a97). The global hook runner correctly reports:

  • getGlobalHookRunner() returns a valid runner
  • hasHooks("message_sending")true
  • registry.typedHooks.filter(h => h.hookName === "message_sending").length1

The hook IS registered and visible to the runner — it is simply never called by the Discord delivery code path.

Discord's reply delivery in route-resolution-CRUBXA7Y.js calls sendDiscordChunkWithFallback() directly (via REST or webhook), with no hookRunner.runMessageSending() invocation anywhere in that path.

Suggested fix

Add a getGlobalHookRunner().runMessageSending(...) call in the Discord reply delivery path, similar to how Telegram handles it. The hook result should be checked for { cancel: true } and { content: string } overrides before sending each chunk.

Workaround

Using before_agent_start hook with appendSystemContext to inject a formatting instruction as a soft prevention layer. This works but is not guaranteed since the LLM may still generate tables when the user explicitly requests them.

extent analysis

TL;DR

Add a getGlobalHookRunner().runMessageSending(...) call in the Discord reply delivery path to invoke message_sending hooks.

Guidance

  • Identify the Discord reply delivery code path in route-resolution-CRUBXA7Y.js and locate where sendDiscordChunkWithFallback() is called.
  • Insert a call to getGlobalHookRunner().runMessageSending(...) before sending each chunk, passing the necessary event data.
  • Check the hook result for { cancel: true } and { content: string } overrides to determine whether to send the original or modified content.
  • Verify that the message_sending hook is correctly registered and visible to the global hook runner.

Example

const hookRunner = getGlobalHookRunner();
const event = { content: 'Original message content' };
const hookResult = hookRunner.runMessageSending(event);
if (hookResult.cancel) {
  // Do not send the message
} else if (hookResult.content) {
  // Send the modified message content
  sendDiscordChunkWithFallback(hookResult.content);
} else {
  // Send the original message content
  sendDiscordChunkWithFallback(event.content);
}

Notes

This fix assumes that the getGlobalHookRunner() function returns a valid hook runner instance and that the message_sending hook is correctly registered.

Recommendation

Apply the suggested fix by adding the getGlobalHookRunner().runMessageSending(...) call in the Discord reply delivery path, as this will ensure that message_sending hooks are invoked for all outgoing Discord messages.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

message_sending hooks should fire for all outgoing messages regardless of channel, consistent with:

  • Telegram delivery (delivery-*.js line ~522): calls getGlobalHookRunner()runMessageSending()
  • General delivery path (deliver-*.js line ~835): calls getGlobalHookRunner()runMessageSending()

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING