openclaw - 💡(How to fix) Fix Discord progress draft is deleted after final error reply

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…

When Discord progress draft previews are enabled, a final error reply can cause the previous progress/status draft message to be deleted during cleanup.

The final error message is delivered as a separate final reply, but the draft preview controller is not marked as having delivered a final reply when payload.isError === true. As a result, cleanup() treats the turn as if no final reply was delivered and clears the existing progress draft.

Error Message

  1. A Discord turn starts and OpenClaw creates/updates a progress draft preview message.
  2. The run eventually returns a final error reply.
  3. OpenClaw sends the final error reply.
  4. The previous progress/status draft message is deleted by draft cleanup.

Root Cause

This makes it harder to inspect what happened before the failure, because the visible progress/status history disappears at the end of the turn.

Fix Action

Fix / Workaround

Local hotfix tested

I applied the following local dist hotfix:

node --check dist/message-handler.process-Cs0IxLet.js passes after the change, and the gateway was restarted so the hotfix is active locally.

Code Example

if (!finalReplyDelivered) await draftStream?.discardPending();
if (!finalReplyDelivered && !finalizedViaPreviewMessage && draftStream?.messageId()) await draftStream.clear();

---

await deliverDiscordReply(/* ... */);
replyReference.markSent();
if (isFinal && payload.isError !== true) {
  draftPreview.markFinalReplyDelivered();
  observer?.onFinalReplyDelivered?.();
}

---

-if (isFinal && payload.isError !== true) {
+if (isFinal) {
   draftPreview.markFinalReplyDelivered();
   observer?.onFinalReplyDelivered?.();
 }
RAW_BUFFERClick to expand / collapse

Summary

When Discord progress draft previews are enabled, a final error reply can cause the previous progress/status draft message to be deleted during cleanup.

The final error message is delivered as a separate final reply, but the draft preview controller is not marked as having delivered a final reply when payload.isError === true. As a result, cleanup() treats the turn as if no final reply was delivered and clears the existing progress draft.

Observed behavior

  1. A Discord turn starts and OpenClaw creates/updates a progress draft preview message.
  2. The run eventually returns a final error reply.
  3. OpenClaw sends the final error reply.
  4. The previous progress/status draft message is deleted by draft cleanup.

This makes it harder to inspect what happened before the failure, because the visible progress/status history disappears at the end of the turn.

Expected behavior

Once a final error reply has been successfully sent to Discord, the draft preview controller should treat the turn as terminally delivered and should not delete the previous progress/status draft during cleanup.

This issue is separate from a possible future UX improvement where the draft preview could be sealed with a final failed state such as Failed: .... The minimal bug fix is just to prevent cleanup from deleting the draft after a final error reply has been delivered.

Code path

Installed package inspected:

  • openclaw version: 2026.5.22
  • file: dist/message-handler.process-Cs0IxLet.js

Relevant cleanup logic in createDiscordDraftPreviewController():

if (!finalReplyDelivered) await draftStream?.discardPending();
if (!finalReplyDelivered && !finalizedViaPreviewMessage && draftStream?.messageId()) await draftStream.clear();

Relevant delivery logic near the final Discord send path:

await deliverDiscordReply(/* ... */);
replyReference.markSent();
if (isFinal && payload.isError !== true) {
  draftPreview.markFinalReplyDelivered();
  observer?.onFinalReplyDelivered?.();
}

Because final error replies skip markFinalReplyDelivered(), cleanup later sees finalReplyDelivered === false and clears the progress draft.

Local hotfix tested

I applied the following local dist hotfix:

-if (isFinal && payload.isError !== true) {
+if (isFinal) {
   draftPreview.markFinalReplyDelivered();
   observer?.onFinalReplyDelivered?.();
 }

node --check dist/message-handler.process-Cs0IxLet.js passes after the change, and the gateway was restarted so the hotfix is active locally.

Suggested fix

For a source-level fix, mark final error replies as terminally delivered after deliverDiscordReply() succeeds.

Possible implementation options:

  • Treat any successfully delivered final reply, including payload.isError === true, as finalReplyDelivered.
  • Or introduce a more precise flag name such as finalResponseDelivered / finalTerminalReplyDelivered if the current finalReplyDelivered name was intended to mean only non-error assistant replies.

The first option is likely the smallest behavioral fix.

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

Once a final error reply has been successfully sent to Discord, the draft preview controller should treat the turn as terminally delivered and should not delete the previous progress/status draft during cleanup.

This issue is separate from a possible future UX improvement where the draft preview could be sealed with a final failed state such as Failed: .... The minimal bug fix is just to prevent cleanup from deleting the draft after a final error reply has been delivered.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING