Skip to content

fix(discord): fetchChannel called before reactionMode check causes 30-73s slow listeners on gateway reconnect #73133

Description

@isaacsummers

Summary

In `handleDiscordReactionEvent`, `client.fetchChannel(data.channel_id)` is called unconditionally at line 475 — before any `reactionMode` check. When the Discord gateway disconnects and reconnects, queued `MESSAGE_REACTION_ADD` events fire simultaneously, all calling `fetchChannel` at once. This hits Discord REST rate limits and each call takes 30–73s before timing out, triggering cascading `Slow listener detected` warnings and session lock congestion.

This is distinct from #47516 (which covers unconditional listener registration when `reactionNotifications: "off"`). This bug affects any config using `reactionNotifications: "allowlist"` or `"all"`.

Environment

  • OpenClaw: 2026.4.25
  • OS: Ubuntu 24.04 (linux/amd64)
  • Node: v22
  • Discord: 2 guilds, both `reactionNotifications: "allowlist"`
  • Multi-agent setup (~4 active sessions)

Root Cause

In `dist/extensions/discord/provider-C7FYFXVp.js`:

```js
async function handleDiscordReactionEvent(params) {
// ...
if (isGuildMessage && guildEntries && Object.keys(guildEntries).length > 0 && !guildInfo) return; // line 474 — last cheap exit

const channel = await client.fetchChannel(data.channel_id); // line 475 — REST call fires unconditionally
// ...
// reactionMode check only happens much later inside handleDiscordThreadReactionNotification / handleDiscordChannelReactionNotification
}
```

The `reactionMode === "off"` early-exit lives inside the downstream handlers (`handleDiscordThreadReactionNotification` line 417, `handleDiscordChannelReactionNotification` line 442) — but `fetchChannel` runs before reaching them for all reaction modes, including `"allowlist"`.

Reproduction

  1. Configure 2+ guilds with `reactionNotifications: "allowlist"`
  2. Let the Discord gateway disconnect (network blip, timeout, etc.)
  3. Several reaction events queue up during the ~30-40s reconnect window
  4. On reconnect, all queued events fire simultaneously
  5. Each calls `fetchChannel` concurrently → Discord REST rate limits → each call stalls 30–73s
  6. `Slow listener detected: DiscordReactionListener durationMs=36000+` logged repeatedly
  7. Session write locks held past max (15s), sessions stall, auto-restart triggered

Log evidence

```
[warn] Slow listener detected: {"listener":"DiscordReactionListener","event":"MESSAGE_REACTION_ADD","durationMs":36184}
[warn] Slow listener detected: {"listener":"DiscordReactionListener","event":"MESSAGE_REACTION_ADD","durationMs":36271}
[warn] Slow listener detected: {"listener":"DiscordReactionListener","event":"MESSAGE_REACTION_ADD","durationMs":36300}
[warn] session-write-lock held for 91000ms (max=15000)
[info] [agent-name] auto-restart attempt 1/10
```

Suggested Fix

Move the `reactionMode` check to before the `fetchChannel` call:

```js
async function handleDiscordReactionEvent(params) {
// ... existing cheap checks ...
if (isGuildMessage && guildEntries && Object.keys(guildEntries).length > 0 && !guildInfo) return;

// Early exit before any REST call when reactions are disabled
const reactionMode = guildInfo?.reactionNotifications ?? "own";
if (reactionMode === "off") return;

const channel = await client.fetchChannel(data.channel_id); // now only called when reactions are enabled
// ...
}
```

For `"allowlist"` mode, it's also worth checking the user against the allowlist before calling `fetchChannel`, since the channel info isn't needed to make that determination.

Impact

  • Every reaction event in `"allowlist"` or `"all"` mode makes a live Discord REST call
  • After a gateway reconnect, burst of queued reactions causes concurrent `fetchChannel` calls → rate limit stalls → session lock cascade → agent auto-restarts
  • Affects stability of multi-agent setups on Discord

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions