openclaw - ✅(Solved) Fix [Bug] Message tool sends duplicate text + card when both message and card params are present [1 pull requests, 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#80244Fetched 2026-05-11 03:17:08
View on GitHub
Comments
2
Participants
2
Timeline
6
Reactions
2
Author
Timeline (top)
commented ×2closed ×1cross-referenced ×1mentioned ×1

Root Cause

The message (text) parameter is passed through to channel plugins alongside the card parameter without any normalization. Channel plugins like Feishu/Lark see both text and card are non-empty and send them as separate messages, because Feishu msg_type=interactive (card) does not support inline text content.

The message param has dual semantics:

  • Without card: message body text (correct behavior: send as text)
  • With card: LLMs use it as notification preview / card title (incorrect behavior: sent as separate text message)

Fix Action

Workaround

Plugin-level fix: in the channel plugin deliverMessage, skip text send when card is present. This has been verified working for Feishu/Lark but needs to be applied to each plugin independently.

PR fix notes

PR #80245: fix: strip message/text param when card is present in message tool send

Description (problem / solution / changelog)

Summary

When an agent calls message(action="send", message="Title", card={...}), channel plugins receive both text and card and send them as separate messages — the user sees a duplicate text message before the card.

Changes

src/infra/outbound/message-action-normalization.ts

  • Added card-aware text sanitization in normalizeMessageActionInput()
  • When action === "send" and card is a non-null object:
    • Moves message/text to _cardNotificationHint (for push notification previews)
    • Deletes message/text from args so plugins do not send standalone text
  • Only applies to send action; other actions (edit, etc.) are unaffected

src/infra/outbound/message-action-normalization.test.ts

  • 6 new test cases covering:
    • message cleared when card present
    • text cleared when card present
    • message preserved when no card
    • Non-send actions unaffected
    • Empty message handled gracefully
    • message preferred over text for hint

Motivation

  • All card-capable channels (Feishu, Teams, Slack, Discord) benefit from a single core fix
  • LLM behavior is unpredictable — prompt-level fixes ("pass empty message") are unreliable
  • Plugin contract is cleaner: when card is present, plugins can trust there is no separate text to send

Backward Compatibility

  • _cardNotificationHint is an optional metadata field — plugins can read it for push notification previews
  • Existing plugins that do not reference _cardNotificationHint are unaffected
  • Plugin-level workarounds (skipping text when card present) remain compatible

Fixes #80244

Changed files

  • src/infra/outbound/message-action-normalization.test.ts (modified, +80/-0)
  • src/infra/outbound/message-action-normalization.ts (modified, +18/-0)

Code Example

message(action="send", channel="feishu", target="user:ou_xxx", 
           message="Daily Report", 
           card={"header":{"title":{"tag":"plain_text","content":"Daily Report"}},"elements":[...]})

---

// When card is present, clear message/text to prevent duplicate sends.
// Preserve as _cardNotificationHint for push notification previews.
if (args.card != null && typeof args.card === "object") {
  if (args.message) {
    args._cardNotificationHint = args.message;
    delete args.message;
  }
  if (args.text) {
    args._cardNotificationHint ??= args.text;
    delete args.text;
  }
}
RAW_BUFFERClick to expand / collapse

Bug Description

When an agent calls message(action="send", message="Title Text", card={...}), channels that support interactive cards (e.g., Feishu/Lark) send two messages to the user:

  1. A plain text message with the message param content
  2. The interactive card

The user sees a duplicate — the text message appears immediately before the card, and both contain the same title.

Root Cause

The message (text) parameter is passed through to channel plugins alongside the card parameter without any normalization. Channel plugins like Feishu/Lark see both text and card are non-empty and send them as separate messages, because Feishu msg_type=interactive (card) does not support inline text content.

The message param has dual semantics:

  • Without card: message body text (correct behavior: send as text)
  • With card: LLMs use it as notification preview / card title (incorrect behavior: sent as separate text message)

Steps to Reproduce

  1. Configure a Feishu/Lark channel
  2. In an isolated cron job or agent session, call:
    message(action="send", channel="feishu", target="user:ou_xxx", 
            message="Daily Report", 
            card={"header":{"title":{"tag":"plain_text","content":"Daily Report"}},"elements":[...]})
  3. User receives TWO messages: "Daily Report" as text + the card

Expected Behavior

Only the card should be sent. The message param should be treated as notification metadata (push notification preview), not as a standalone text message.

Proposed Fix

Add card-aware parameter normalization in the core message tool dispatcher (src/infra/outbound/message-action-runner.ts), before args are passed to channel plugins:

// When card is present, clear message/text to prevent duplicate sends.
// Preserve as _cardNotificationHint for push notification previews.
if (args.card != null && typeof args.card === "object") {
  if (args.message) {
    args._cardNotificationHint = args.message;
    delete args.message;
  }
  if (args.text) {
    args._cardNotificationHint ??= args.text;
    delete args.text;
  }
}

Why fix in core, not plugins:

  1. All card-capable channels benefit (Feishu, Teams, Slack, Discord)
  2. Single enforcement point — no per-plugin fixes needed
  3. LLM behavior is unpredictable — prompt-level fixes ("pass empty message") are unreliable
  4. Cleaner plugin contract — plugins can trust that card presence means no separate text

Workaround

Plugin-level fix: in the channel plugin deliverMessage, skip text send when card is present. This has been verified working for Feishu/Lark but needs to be applied to each plugin independently.

Environment

  • OpenClaw version: latest (2026.5.x)
  • Channel: Feishu/Lark (openclaw-lark extension)
  • Trigger: cron jobs with message(action=send) tool calls using card param

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 - ✅(Solved) Fix [Bug] Message tool sends duplicate text + card when both message and card params are present [1 pull requests, 2 comments, 2 participants]