openclaw - 💡(How to fix) Fix Discord: implement messages.statusReactions lifecycle (done emoji persistence) — currently Telegram-only despite docs [2 comments, 2 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#78431Fetched 2026-05-07 03:36:58
View on GitHub
Comments
2
Participants
2
Timeline
10
Reactions
2
Timeline (top)
mentioned ×4subscribed ×4commented ×2

The docs at docs/gateway/config-agents.md and docs/concepts/qa-e2e-automation.md state:

messages.statusReactions.enabled enables lifecycle status reactions on Slack, Discord, and Telegram.

Reading the bundled code, only Telegram actually wires the full lifecycle (queued → thinking → tool/coding/web → done/error → hold → cleanup). Discord uses only the legacy single-emoji ackReaction + cleanup-on-reply path. There is no Discord equivalent of the Telegram finalizeTelegramStatusReaction function. Setting messages.statusReactions = { enabled: true, emojis: { done: "✅" }, timing: { doneHoldMs: 3600000 } } has no observable effect on Discord guild channels — replies still cycle exactly like the bare ackReaction setup.

This is a follow-up to #78405 (silent text-reply drops) and represents the inverse problem: documented feature, missing implementation.

Error Message

Reading the bundled code, only Telegram actually wires the full lifecycle (queued → thinking → tool/coding/web → done/error → hold → cleanup). Discord uses only the legacy single-emoji ackReaction + cleanup-on-reply path. There is no Discord equivalent of the Telegram finalizeTelegramStatusReaction function. Setting messages.statusReactions = { enabled: true, emojis: { done: "✅" }, timing: { doneHoldMs: 3600000 } } has no observable effect on Discord guild channels — replies still cycle exactly like the bare ackReaction setup. // error path... 3. Add finalizeDiscordStatusReaction(params) mirroring the Telegram one — same outcome === "done" / error branches, same doneHoldMs / errorHoldMs semantics.

Root Cause

  • removeAckAfterReply: false → 🤔 reappears immediately after the reply (the runtime calls restoreInitial() because there is no done transition path), so the user just sees 🤔 again, indistinguishable from "still thinking".
  • removeAckAfterReply: true → all reactions cleaned up post-reply, no visual signal that the turn completed successfully.

Fix Action

Fix / Workaround

  • OpenClaw 2026.5.5 (also tested 2026.5.4 with same result)
  • Discord: bot in a personal guild, requireMention: false, multiple bound agents (one slow Ollama-backed, others fast Lobster pipelines)
  • Workaround currently in use: statusReactions.enabled: false, ackReaction: "🤔", ackReactionScope: "all", removeAckAfterReply: true. Visual: 🤔 during processing, removed when reply lands. Acceptable but not what the docs describe.

Happy to test patches on main. Thanks!

Code Example

{
  "messages": {
    "ackReaction": "🤔",
    "ackReactionScope": "all",
    "removeAckAfterReply": false,
    "statusReactions": {
      "enabled": true,
      "emojis": { "done": "✅" },
      "timing": { "doneHoldMs": 3600000 }
    }
  }
}

---

const finalizeTelegramStatusReaction = async (params) => {
    if (!statusReactionController) return;
    if (params.outcome === "done") {
        await statusReactionController.setDone();
        if (removeAckAfterReply) {
            await sleepWithAbort(statusReactionTiming.doneHoldMs);
            await clearTelegramStatusReaction();
        } else {
            await statusReactionController.restoreInitial();
        }
        return;
    }
    // error path...
};
RAW_BUFFERClick to expand / collapse

Discord: implement messages.statusReactions lifecycle (done emoji persistence) — currently Telegram-only despite docs

Summary

The docs at docs/gateway/config-agents.md and docs/concepts/qa-e2e-automation.md state:

messages.statusReactions.enabled enables lifecycle status reactions on Slack, Discord, and Telegram.

Reading the bundled code, only Telegram actually wires the full lifecycle (queued → thinking → tool/coding/web → done/error → hold → cleanup). Discord uses only the legacy single-emoji ackReaction + cleanup-on-reply path. There is no Discord equivalent of the Telegram finalizeTelegramStatusReaction function. Setting messages.statusReactions = { enabled: true, emojis: { done: "✅" }, timing: { doneHoldMs: 3600000 } } has no observable effect on Discord guild channels — replies still cycle exactly like the bare ackReaction setup.

This is a follow-up to #78405 (silent text-reply drops) and represents the inverse problem: documented feature, missing implementation.

User-visible impact

For agents that take a while to respond (e.g. local Ollama models on big contexts: 30–120s), users have no way to distinguish "agent is still working" from "agent crashed silently". Telegram users get the full visual lifecycle (🤔 → 🔥/⚡ → ✅) and can leave a ✅ pinned long after the reply for "this turn was processed successfully" semantics.

Concrete sequence we wanted on Discord and could not get:

  1. User posts a message.
  2. Bot reacts with 🤔 immediately (works today via ackReaction).
  3. While the agent runs tool calls, the reaction transitions to 🔥 (exec) / ⚡ (web search) / 👨‍💻 (coding) — doesn't happen on Discord.
  4. When the reply is sent, the reaction transitions to ✅ — doesn't happen on Discord.
  5. ✅ stays for doneHoldMs then is cleaned up — doneHoldMs has no effect on Discord.

Currently on Discord we are forced to choose between:

  • removeAckAfterReply: false → 🤔 reappears immediately after the reply (the runtime calls restoreInitial() because there is no done transition path), so the user just sees 🤔 again, indistinguishable from "still thinking".
  • removeAckAfterReply: true → all reactions cleaned up post-reply, no visual signal that the turn completed successfully.

Neither produces "you can see at a glance which messages were processed".

Repro

OpenClaw 2026.5.5.

openclaw.json:

{
  "messages": {
    "ackReaction": "🤔",
    "ackReactionScope": "all",
    "removeAckAfterReply": false,
    "statusReactions": {
      "enabled": true,
      "emojis": { "done": "✅" },
      "timing": { "doneHoldMs": 3600000 }
    }
  }
}

Discord guild channel with requireMention: false, agent on local Ollama (slow inference, 30–60s reply time).

Observed: 🤔 appears immediately, no transitions during tool calls, 🤔 reappears right after the text reply is posted (no ✅ phase), doneHoldMs ignored.

Expected (per docs and Telegram parity): 🤔 → optional intermediate emojis → ✅ held for doneHoldMs → cleanup.

Code pointers (where to wire it)

The Telegram implementation is the reference. From dist/bot-D-7bCSXH.js:~4513:

const finalizeTelegramStatusReaction = async (params) => {
    if (!statusReactionController) return;
    if (params.outcome === "done") {
        await statusReactionController.setDone();
        if (removeAckAfterReply) {
            await sleepWithAbort(statusReactionTiming.doneHoldMs);
            await clearTelegramStatusReaction();
        } else {
            await statusReactionController.restoreInitial();
        }
        return;
    }
    // error path...
};

The statusReactionController factory is in dist/channel-feedback-B3QykyPj.js (finishWithEmoji, setDone, setError, etc.) — already provider-agnostic, just needs a Discord adapter (addReaction(channel, msg, emoji) / removeReaction(...)).

The Discord plugin already does single-emoji reactions (the ackReaction path), so the REST surface (PUT /channels/.../messages/.../reactions/.../@me) is wired. Plugging the existing controller into the Discord finalize callback should be small.

grep -rn "statusReactionController" dist/ returns matches only in bot-D-7bCSXH.js (Telegram). No Discord file references it, which confirms the gap.

There's also a deeper UX win: showing an in-progress emoji during long Ollama tool loops would give users a heartbeat. The same setThinking() / setTool() / setCompacting() events are already emitted by the runtime — the Discord plugin just doesn't subscribe.

Suggested implementation

  1. In extensions/discord/src/monitor/message-handler.process.ts (or wherever the Discord deliver finalize hook lives), instantiate the same createStatusReactionController factory used by Telegram, with a Discord-specific reaction adapter.
  2. Wire it into the same lifecycle event stream the Telegram path uses (pipeline.runtime-* emits the events).
  3. Add finalizeDiscordStatusReaction(params) mirroring the Telegram one — same outcome === "done" / error branches, same doneHoldMs / errorHoldMs semantics.
  4. Update docs/channels/discord.md to confirm full feature support, or — if this is intentionally Telegram-first — soften the wording in docs/gateway/config-agents.md to "Slack and Telegram only; Discord uses simplified ackReaction + cleanup-on-reply".

Why this is worth shipping

  • Doc/code mismatch is a trust-erosion issue: someone who reads the docs and configures the full lifecycle, then sees nothing, assumes their config is wrong (we lost ~1h chasing down which key was rejected by the schema).
  • Local-model usability: agents on Ollama / LM Studio can take 30–120s. Lifecycle reactions are exactly the use case where they pay off most. Cloud-model setups (GPT, Claude) reply in 2–5s and don't need it as much, which is probably why this gap survived — a Telegram-cloud user wouldn't notice.
  • Parity is a small change: the runtime emits the events, the controller factory is already abstract, the Discord REST API supports per-message reactions. Mostly wiring + an adapter.

Environment

  • OpenClaw 2026.5.5 (also tested 2026.5.4 with same result)
  • Discord: bot in a personal guild, requireMention: false, multiple bound agents (one slow Ollama-backed, others fast Lobster pipelines)
  • Workaround currently in use: statusReactions.enabled: false, ackReaction: "🤔", ackReactionScope: "all", removeAckAfterReply: true. Visual: 🤔 during processing, removed when reply lands. Acceptable but not what the docs describe.

Happy to test patches on main. Thanks!

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…

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 Discord: implement messages.statusReactions lifecycle (done emoji persistence) — currently Telegram-only despite docs [2 comments, 2 participants]