openclaw - 💡(How to fix) Fix Weixin channel can drop standalone MEDIA directives when payload.mediaUrl is absent [1 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#78697Fetched 2026-05-07 03:33:41
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
2
Author
Timeline (top)
commented ×1mentioned ×1subscribed ×1

For the openclaw-weixin channel, an assistant final reply that contains a standalone MEDIA:/path/to/image.png directive can be delivered as text only because the channel deliver() callback receives a payload without mediaUrl / mediaUrls.

This is different from #75182: the core MEDIA parser handles the directive correctly when tested directly, and there is no polluted-path ENOENT failure. The failure is that the final reply reaches the Weixin channel as text with mediaUrl=none.

Root Cause

For the openclaw-weixin channel, an assistant final reply that contains a standalone MEDIA:/path/to/image.png directive can be delivered as text only because the channel deliver() callback receives a payload without mediaUrl / mediaUrls.

Fix Action

Fix / Workaround

Local workaround that fixed it

Code Example

Here is a test image.

   MEDIA:/home/user/.openclaw/media/outbound/test.png

---

outbound: ... mediaUrl=present
sendImageMessageWeixin: success ...
outbound: media sent OK ...

---

outbound: ... textLen=57 mediaUrl=none
outbound: text sent OK ...

---

outbound: ... mediaUrl=present
  sendImageMessageWeixin: success ...
  outbound: media sent OK ...

---

function extractMediaDirectiveFromText(text: string): { text: string; mediaUrl?: string } {
  let mediaUrl: string | undefined;
  const keptLines: string[] = [];
  for (const line of text.split(/\r?\n/)) {
    const match = /^\s*MEDIA:\s*`?([^`\r\n]+?)`?\s*$/i.exec(line);
    if (match) {
      mediaUrl ??= match[1]?.trim();
      continue;
    }
    keptLines.push(line);
  }
  return { text: keptLines.join("\n").trimEnd(), mediaUrl };
}

// inside deliver(payload)
const rawText = payload.text ?? "";
const directiveMedia = extractMediaDirectiveFromText(rawText);
let text = (() => {
  const f = new StreamingMarkdownFilter();
  return f.feed(directiveMedia.text) + f.flush();
})();
const mediaUrl = payload.mediaUrl ?? payload.mediaUrls?.[0] ?? directiveMedia.mediaUrl;
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

For the openclaw-weixin channel, an assistant final reply that contains a standalone MEDIA:/path/to/image.png directive can be delivered as text only because the channel deliver() callback receives a payload without mediaUrl / mediaUrls.

This is different from #75182: the core MEDIA parser handles the directive correctly when tested directly, and there is no polluted-path ENOENT failure. The failure is that the final reply reaches the Weixin channel as text with mediaUrl=none.

Environment

  • OpenClaw: 2026.5.5 (b1abf9d)
  • Plugin: @tencent-weixin/[email protected]
  • OS: Linux x64
  • Node: v22.22.2

Steps to reproduce

  1. Use the openclaw-weixin channel.

  2. Send an assistant final reply containing a standalone local image MEDIA directive, for example:

    Here is a test image.
    
    MEDIA:/home/user/.openclaw/media/outbound/test.png
  3. Observe the Weixin channel outbound logs and the WeChat client.

Expected behavior

The final reply MEDIA directive should be normalized to payload.mediaUrl / payload.mediaUrls before channel delivery, and the Weixin channel should upload/send the image.

Expected channel log shape:

outbound: ... mediaUrl=present
sendImageMessageWeixin: success ...
outbound: media sent OK ...

Actual behavior

The assistant final reply text contained the standalone MEDIA: line, but the Weixin channel logged mediaUrl=none and sent only text.

Relevant observed log shape:

outbound: ... textLen=57 mediaUrl=none
outbound: text sent OK ...

No sendWeixinMediaFile, upload, or sendImageMessageWeixin call occurred for that reply.

Debugging evidence

  • Directly testing OpenClaw's MEDIA parsing on the same text returns the expected clean path in mediaUrls.

  • Directly calling @tencent-weixin/openclaw-weixin's media-send helper with a 1MB PNG succeeds; the WeChat client receives the image.

  • After adding a local fallback in the Weixin plugin deliver() path to parse standalone MEDIA: lines from payload.text when payload.mediaUrl is absent, the same normal assistant reply path succeeds:

    outbound: ... mediaUrl=present
    sendImageMessageWeixin: success ...
    outbound: media sent OK ...

This suggests the issue is at the OpenClaw core ↔ channel delivery contract boundary: core should consistently normalize reply directives before channel delivery, and/or the Weixin channel should defensively parse leftover standalone MEDIA: directives in payload.text.

Local workaround that fixed it

In @tencent-weixin/openclaw-weixin/src/messaging/process-message.ts, before markdown filtering in deliver(), parse standalone MEDIA: lines from rawText, remove them from the text body, and use the extracted path as a fallback:

function extractMediaDirectiveFromText(text: string): { text: string; mediaUrl?: string } {
  let mediaUrl: string | undefined;
  const keptLines: string[] = [];
  for (const line of text.split(/\r?\n/)) {
    const match = /^\s*MEDIA:\s*`?([^`\r\n]+?)`?\s*$/i.exec(line);
    if (match) {
      mediaUrl ??= match[1]?.trim();
      continue;
    }
    keptLines.push(line);
  }
  return { text: keptLines.join("\n").trimEnd(), mediaUrl };
}

// inside deliver(payload)
const rawText = payload.text ?? "";
const directiveMedia = extractMediaDirectiveFromText(rawText);
let text = (() => {
  const f = new StreamingMarkdownFilter();
  return f.feed(directiveMedia.text) + f.flush();
})();
const mediaUrl = payload.mediaUrl ?? payload.mediaUrls?.[0] ?? directiveMedia.mediaUrl;

With that local fallback, normal MEDIA: replies started delivering images correctly through Weixin.

Notes

There was also a separate edge case where a 154-byte tiny PNG caused a Weixin CDN status 500 during direct plugin upload, while a normal 1MB PNG uploaded and delivered successfully. That appears independent from this MEDIA directive normalization issue.

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

The final reply MEDIA directive should be normalized to payload.mediaUrl / payload.mediaUrls before channel delivery, and the Weixin channel should upload/send the image.

Expected channel log shape:

outbound: ... mediaUrl=present
sendImageMessageWeixin: success ...
outbound: media sent OK ...

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 Weixin channel can drop standalone MEDIA directives when payload.mediaUrl is absent [1 comments, 2 participants]