Skip to content

Discord progress draft is deleted after final error reply #86395

@awaker72

Description

@awaker72

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions