Skip to content

[Bug]: Discord final replies attach Markdown image badges from assistant text #72642

@solavrc

Description

@solavrc

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

Discord final replies can upload incidental Markdown image badges from assistant text as unexpected attachments after the Markdown-image media extraction added for #66191.

Steps to reproduce

  1. On OpenClaw 2026.4.24, pass assistant/final reply text containing a remote Markdown image badge through splitMediaFromOutput() / parseReplyDirectives().
  2. Minimal input:
tech: ![Node.js](https://img.shields.io/badge/Node.js-339933?logo=node.js&logoColor=white)
  1. Observed parser output includes text: "tech:" and mediaUrls: ["https://img.shields.io/badge/Node.js-339933?logo=node.js&logoColor=white"].
  2. Let the normalized final reply deliver through Discord. Discord treats the extracted mediaUrls value as a file attachment.

Expected behavior

Normal Discord final replies should not automatically turn arbitrary Markdown images in assistant prose into Discord file attachments. Incidental Markdown image syntax from fetched or summarized content should remain text unless the agent or a trusted tool explicitly requests media delivery.

Actual behavior

The shared final-reply normalization path extracts remote Markdown image URLs from assistant text into mediaUrls. Discord then sends those URLs as attachments. In observed Discord replies, README badge URLs produced unexpected attachments named after badge targets such as Node.js-339933 and Playwright-2EAD33.

This can also cause final reply delivery failures when an illustrative Markdown image example contains an invalid or unresolvable URL, because the URL is promoted to media and the delivery layer tries to fetch it.

OpenClaw version

2026.4.24

Operating system

macOS local gateway host

Install method

npm global openclaw package

Model

Not model-dependent; reproduced directly at the parser/final-reply normalization layer.

Provider / routing chain

Not provider-dependent; the issue occurs after assistant text generation in outbound final reply normalization before Discord delivery.

Additional provider/model setup details

The local agent was using the normal Discord final reply path. The same extraction reproduces by calling the parser directly, without relying on model behavior or custom workspace instructions.

Logs, screenshots, and evidence

Related upstream change: #66471, which added Markdown image extraction for #66191.

Relevant path observed locally:

  • parseReplyDirectives() calls splitMediaFromOutput() unconditionally.
  • createOutboundPayloadPlanEntry() uses parsed mediaUrls.
  • Discord outbound delivery sends those mediaUrls as attachments.

2026.4.25-beta.10 appears to improve invalid/internal/localhost URL filtering, but real HTTPS image URLs such as shields.io badges are still lifted into mediaUrls.

Impact and severity

Affected users/systems/channels: Discord channels using normal agent final replies.
Severity: annoying, and it can block reply delivery in the invalid-URL case.
Frequency: deterministic for remote Markdown image syntax that passes media validation.
Consequence: unexpected Discord attachments and possible final reply delivery failure.

Additional information

Likely root cause: #66471 extended src/media/parse.ts so splitMediaFromOutput() recognizes Markdown image targets and lifts them into media segments. That behavior was added for Telegram group media delivery but now affects Discord final replies via the shared final-reply normalization path.

Suggested fix directions:

  1. Make Markdown-image lifting channel/context aware and disable it for normal Discord final replies.
  2. Restrict Markdown-image lifting to the Telegram path that needed Telegram group chats strip media — images only work in DMs #66191.
  3. Only lift Markdown images to outbound media when explicitly requested by a trusted tool/directive.
  4. Add tests covering Discord final replies with remote Markdown image badges.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    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