openclaw - 💡(How to fix) Fix Bug: inbound Discord messages bypass RECENT_QUEUE_MESSAGE_IDS dedup cache [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

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#58658Fetched 2026-04-08 01:59:39
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
0

Normal inbound Discord messages are never checked against the RECENT_QUEUE_MESSAGE_IDS dedup cache, allowing the same message to be processed multiple times (e.g. after a gateway restart or on Discord RESUME replay).

Root Cause

There are two distinct dispatch paths for Discord messages:

Path A — Follow-up / queue turns (via enqueueFollowupRun):

runReplyAgent → enqueueFollowupRun → RECENT_QUEUE_MESSAGE_IDS.peek() ✅

Path B — Normal inbound messages (every message a user sends):

Discord event → debouncer.enqueue() → onFlush() → preflightDiscordMessage() → inboundWorker.enqueue(buildDiscordInboundJob(ctx))

Path B has no dedup check at any point. RECENT_QUEUE_MESSAGE_IDS is only consulted inside enqueueFollowupRun (src/auto-reply/reply/queue/enqueue.ts), which Path B never calls.

Fix Action

Fix / Workaround

There are two distinct dispatch paths for Discord messages:

  • On Discord RESUME events, recent messages are replayed by the Discord gateway with the same snowflake IDs — Path B processes them again
  • After a gateway restart (which clears the in-process cache), the same message can be dispatched twice if it was in-flight at restart
  • Under any condition where the Discord listener fires the same event more than once, the message will be processed without duplicate suppression

Code Example

runReplyAgent → enqueueFollowupRun → RECENT_QUEUE_MESSAGE_IDS.peek()
---

Discord event → debouncer.enqueue()onFlush()preflightDiscordMessage() → inboundWorker.enqueue(buildDiscordInboundJob(ctx))

---

// Near top of preflightDiscordMessage, after resolving messageChannelId:
const dedupKey = buildRecentMessageIdKey({ messageId: message.id, originatingChannel: discord, originatingAccountId: params.accountId, ... });
if (dedupKey && RECENT_QUEUE_MESSAGE_IDS.peek(dedupKey)) {
  logVerbose(`discord: drop duplicate inbound message ${message.id}`);
  return null;
}
// ... rest of preflight ...
// Before returning ctx:
if (dedupKey) RECENT_QUEUE_MESSAGE_IDS.check(dedupKey);
return ctx;
RAW_BUFFERClick to expand / collapse

Summary

Normal inbound Discord messages are never checked against the RECENT_QUEUE_MESSAGE_IDS dedup cache, allowing the same message to be processed multiple times (e.g. after a gateway restart or on Discord RESUME replay).

Root cause

There are two distinct dispatch paths for Discord messages:

Path A — Follow-up / queue turns (via enqueueFollowupRun):

runReplyAgent → enqueueFollowupRun → RECENT_QUEUE_MESSAGE_IDS.peek() ✅

Path B — Normal inbound messages (every message a user sends):

Discord event → debouncer.enqueue() → onFlush() → preflightDiscordMessage() → inboundWorker.enqueue(buildDiscordInboundJob(ctx))

Path B has no dedup check at any point. RECENT_QUEUE_MESSAGE_IDS is only consulted inside enqueueFollowupRun (src/auto-reply/reply/queue/enqueue.ts), which Path B never calls.

Evidence

From discord-CcCLMjHw.js (compiled dist):

  • RECENT_QUEUE_MESSAGE_IDS defined at line 78640 with TTL=300s, maxSize=10000
  • Checked at lines 78667/78681 inside enqueueFollowupRun only
  • preflightDiscordMessage (line 132019): no dedup check before returning ctx
  • createDiscordInboundWorker.enqueue() (lines 132592, 132626): jobs enqueued with no prior cache lookup
  • message.id (Discord snowflake) is available in preflightDiscordMessage context throughout

Impact

  • On Discord RESUME events, recent messages are replayed by the Discord gateway with the same snowflake IDs — Path B processes them again
  • After a gateway restart (which clears the in-process cache), the same message can be dispatched twice if it was in-flight at restart
  • Under any condition where the Discord listener fires the same event more than once, the message will be processed without duplicate suppression

Expected behavior

preflightDiscordMessage (or the onFlush handler in the debouncer) should check RECENT_QUEUE_MESSAGE_IDS by message.id before proceeding. If the message ID is already in the cache, return null (drop). If not, mark it before handing off to inboundWorker.enqueue.

Suggested fix location

src/discord/monitor/message-handler.process.ts (or equivalent pre-compiled source for preflightDiscordMessage):

// Near top of preflightDiscordMessage, after resolving messageChannelId:
const dedupKey = buildRecentMessageIdKey({ messageId: message.id, originatingChannel: discord, originatingAccountId: params.accountId, ... });
if (dedupKey && RECENT_QUEUE_MESSAGE_IDS.peek(dedupKey)) {
  logVerbose(`discord: drop duplicate inbound message ${message.id}`);
  return null;
}
// ... rest of preflight ...
// Before returning ctx:
if (dedupKey) RECENT_QUEUE_MESSAGE_IDS.check(dedupKey);
return ctx;

Alternatively, the check could be placed in the debouncer onFlush handler before calling preflightDiscordMessage.

Environment

  • OpenClaw version: checked against installed dist in /usr/lib/node_modules/openclaw/dist/discord-CcCLMjHw.js
  • Analyzed via static grep of compiled bundle
  • Confirmed: processDiscordMessage is only ever reached via processDiscordInboundJobcreateDiscordInboundWorker.enqueue → the Path B chain above

extent analysis

TL;DR

To fix the issue of duplicate Discord message processing, add a deduplication check using RECENT_QUEUE_MESSAGE_IDS in the preflightDiscordMessage function or the debouncer's onFlush handler.

Guidance

  • Identify the correct location for the deduplication check, either in preflightDiscordMessage or the debouncer's onFlush handler, based on the code structure and requirements.
  • Implement the deduplication check using RECENT_QUEUE_MESSAGE_IDS and the message.id to prevent processing of duplicate messages.
  • Consider logging or monitoring the number of duplicate messages dropped to ensure the fix is effective.
  • Review the code to ensure that the RECENT_QUEUE_MESSAGE_IDS cache is properly updated and maintained to prevent false positives or negatives.

Example

const dedupKey = buildRecentMessageIdKey({ messageId: message.id, originatingChannel: discord, originatingAccountId: params.accountId, ... });
if (dedupKey && RECENT_QUEUE_MESSAGE_IDS.peek(dedupKey)) {
  logVerbose(`discord: drop duplicate inbound message ${message.id}`);
  return null;
}

Notes

The suggested fix assumes that the RECENT_QUEUE_MESSAGE_IDS cache is properly implemented and maintained. It's essential to review the cache's configuration, such as its TTL and maxSize, to ensure it's suitable for the application's requirements.

Recommendation

Apply the workaround by adding the deduplication check in the preflightDiscordMessage function or the debouncer's onFlush handler, as this will prevent duplicate message processing and ensure the correct functionality of the Discord message handler.

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

preflightDiscordMessage (or the onFlush handler in the debouncer) should check RECENT_QUEUE_MESSAGE_IDS by message.id before proceeding. If the message ID is already in the cache, return null (drop). If not, mark it before handing off to inboundWorker.enqueue.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix Bug: inbound Discord messages bypass RECENT_QUEUE_MESSAGE_IDS dedup cache [1 participants]