openclaw - 💡(How to fix) Fix [BUG] (feishu) Streaming cards silently truncate long plain-text replies due to Feishu card markdown content limits

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…

Since commit 3b8ab4e11232 ("fix(feishu): stream plain replies as cards", v2026.5.30-beta.1), all plain-text assistant replies are routed through interactive cards (sendMarkdownCardFeishu) instead of plain text messages (sendMessageFeishu) when channels.feishu.streaming is true (default).

Feishu interactive card markdown elements have an implicit content length limit (~10,000 characters per Feishu card schema specification). When the accumulated streaming content exceeds this limit, the Feishu API accepts the card update without error but silently drops the excess content. The OpenClaw log reports streaming as successful (no error path exists for this case), but the visible reply in Feishu is truncated.

Before this commit, the same reply would be sent as a Feishu plain-text message(s) respecting channels.feishu.textChunkLimit, which has no such content truncation issue. The complete reply is always delivered.

Error Message

Feishu interactive card markdown elements have an implicit content length limit (~10,000 characters per Feishu card schema specification). When the accumulated streaming content exceeds this limit, the Feishu API accepts the card update without error but silently drops the excess content. The OpenClaw log reports streaming as successful (no error path exists for this case), but the visible reply in Feishu is truncated. | Feishu Card API (markdown element) | ~10,000 chars per element | Silently truncates, no error returned |

4. No error handling path exists

  1. Error detection: Monitor for silent truncation by comparing the length of text sent vs. the card's known maxLength limit, and log a warning when truncation is suspected.

Root Cause

Root Cause Analysis

Fix Action

Workaround

Set channels.feishu.streaming: false to disable card streaming and fall back to plain-text message delivery:

{
  channels: {
    feishu: {
      streaming: false,
    },
  },
}

This restores the pre-v2026.5.30-beta.1 behavior where all plain-text replies are sent as Feishu text messages with no content truncation.

Code Example

// channel.runtime-CmCCekKl.js, line 102-103
function shouldUseCard(text) {
    return /

---

Returns `true` if the text contains:
- Markdown code blocks (triple backticks)
- Markdown table syntax

In `"auto"` render mode (default), any reply with code blocks or tables is automatically sent as a card, even without explicit tool card construction.

### 3. Card structure and limits

The card is built by `buildMarkdownCard()`:

---

The outbound system advertises a `maxLength: 4000` limit for card text:

---

But this `textChunkLimit` is only used for **non-streaming** text chunking. In **streaming mode**, the markdown `content` field accumulates text without chunking, and the Feishu API enforces its own limit silently:

| Source | Documented limit | Behavior on overflow |
|--------|-----------------|---------------------|
| Feishu Card API (markdown element) | ~10,000 chars per element | Silently truncates, **no error returned** |
| OpenClaw `feishuOutbound.limits.text.maxLength` | 4,000 chars | Only checked in non-streaming chunking path |
| `textChunkLimit` | 4,000 chars | Only applied in non-streaming chunking path |

### 4. No error handling path exists

When streaming accumulates content beyond Feishu's card limit:
- The Feishu API returns HTTP 200 with no truncation indicator
- OpenClaw logs show `"Started streaming"``"dispatch complete"``"Closed streaming"` with no errors
- The card appears complete from OpenClaw's perspective
- The user sees truncated content with no indication that data was lost

### 5. Non-streaming path comparison

When `streaming: false`:
- Replies are sent via the text message path, not the card path
- The chunker properly respects `textChunkLimit` (or configured value)
- Text messages in Feishu have significantly higher content limits
- Complete content is always delivered

## Configuration that does NOT fix this

- Changing `textChunkLimit` to a larger value has no effect on streaming mode — the value is only used by the chunker in non-streaming paths
- The card `maxLength` in `presentationCapabilities` (4000) is also only checked in the non-streaming path

## Workaround

Set `channels.feishu.streaming: false` to disable card streaming and fall back to plain-text message delivery:

---

This restores the pre-v2026.5.30-beta.1 behavior where all plain-text replies are sent as Feishu text messages with no content truncation.

## Suggested Fixes (upstream)

1. **Pre-card content length check**: Before creating/updating a streaming card, check if the accumulated text exceeds a safe threshold (e.g., 8000 chars). If so, close the streaming card early and deliver the remainder as a plain text reply.

2. **Config-driven fallback**: Add a config option like `channels.feishu.renderMode` with values `"auto"` (current default), `"card"` (always use cards), or `"text"` (always use text messages, pre-v2026.5.30-beta.1 behavior).

3. **Multi-card splitting**: When content exceeds the card markdown limit, split across multiple sequential cards.

4. **Error detection**: Monitor for silent truncation by comparing the length of text sent vs. the card's known `maxLength` limit, and log a warning when truncation is suspected.

## Verification of Commit

---

Changed files:
- `extensions/feishu/src/reply-dispatcher.ts`
- `extensions/feishu/src/reply-dispatcher.test.ts`
- `extensions/feishu/src/streaming-card.ts`

## Log Evidence

Streaming events from an affected session (no errors, no truncation warnings):
RAW_BUFFERClick to expand / collapse

Summary

Since commit 3b8ab4e11232 ("fix(feishu): stream plain replies as cards", v2026.5.30-beta.1), all plain-text assistant replies are routed through interactive cards (sendMarkdownCardFeishu) instead of plain text messages (sendMessageFeishu) when channels.feishu.streaming is true (default).

Feishu interactive card markdown elements have an implicit content length limit (~10,000 characters per Feishu card schema specification). When the accumulated streaming content exceeds this limit, the Feishu API accepts the card update without error but silently drops the excess content. The OpenClaw log reports streaming as successful (no error path exists for this case), but the visible reply in Feishu is truncated.

Before this commit, the same reply would be sent as a Feishu plain-text message(s) respecting channels.feishu.textChunkLimit, which has no such content truncation issue. The complete reply is always delivered.

Environment

  • OpenClaw: v2026.5.30-beta.1 (effbaeb)
  • Channel: feishu (websocket, connectionMode: websocket)
  • Plugin: @openclaw/[email protected]
  • Feishu API domain: feishu (domestic)
  • Not reproducible on versions prior to v2026.5.30-beta.1 (confirmed on v2026.5.26)

Steps to Reproduce

  1. Configure Feishu channel with default settings (streaming: true is the default)
  2. Send a message that triggers a long AI response (>~10,000 chars, e.g., "show the full diagnostic report")
  3. Observe the Feishu interactive card reply — content appears truncated/incomplete
  4. Set channels.feishu.streaming: false (or downgrade to pre-v2026.5.30-beta.1)
  5. Send the same message again — the complete reply is now delivered as a plain text message

Affected users

Any user with channels.feishu.streaming: true (default) who receives AI responses exceeding the Feishu card markdown content limit. Particularly noticeable with:

  • Detailed diagnostic/status reports
  • Long code snippets
  • Structured analysis with tables
  • Any reply containing code blocks (these trigger shouldUseCard to return true in "auto" mode)

Root Cause Analysis

1. Behavioral change introduced by commit 3b8ab4e11232

Before commit 3b8ab4e11232:

  • Plain-text replies → sendMessageFeishu() → Feishu text message
  • Only structured content (cards built by tools) used sendMarkdownCardFeishu()
  • Text messages have no content truncation issue at this scale

After commit 3b8ab4e11232:

  • Plain-text replies → sendMarkdownCardFeishu() → Feishu interactive card (streaming)
  • The commit replaced the plain-text outbound path with a card-based streaming path
  • Cards silently truncate long content

2. The shouldUseCard auto-detection function

// channel.runtime-CmCCekKl.js, line 102-103
function shouldUseCard(text) {
    return /```[\s\S]*?```/.test(text) || /\|.+\|[\r\n]+\|[-:| ]+\|/.test(text);
}

Returns true if the text contains:

  • Markdown code blocks (triple backticks)
  • Markdown table syntax

In "auto" render mode (default), any reply with code blocks or tables is automatically sent as a card, even without explicit tool card construction.

3. Card structure and limits

The card is built by buildMarkdownCard():

// send-WRPbKn3K.js, line 1126
function buildMarkdownCard(text) {
    return {
        schema: "2.0",
        config: { width_mode: "fill" },
        body: { elements: [{
            tag: "markdown",
            content: text          // no size check, no truncation here
        }] }
    };
}

The outbound system advertises a maxLength: 4000 limit for card text:

// channel.runtime-CmCCekKl.js, line 351-358
const feishuOutbound = {
    deliveryMode: "direct",
    chunker: chunkTextForOutbound,
    chunkerMode: "markdown",
    textChunkLimit: 4e3,         // 4000 chars per chunk
    presentationCapabilities: {
        limits: {
            text: {
                maxLength: 4e3,  // 4000 chars
                encoding: "characters",
                markdownDialect: "markdown"
            }
        }
    }
};

But this textChunkLimit is only used for non-streaming text chunking. In streaming mode, the markdown content field accumulates text without chunking, and the Feishu API enforces its own limit silently:

SourceDocumented limitBehavior on overflow
Feishu Card API (markdown element)~10,000 chars per elementSilently truncates, no error returned
OpenClaw feishuOutbound.limits.text.maxLength4,000 charsOnly checked in non-streaming chunking path
textChunkLimit4,000 charsOnly applied in non-streaming chunking path

4. No error handling path exists

When streaming accumulates content beyond Feishu's card limit:

  • The Feishu API returns HTTP 200 with no truncation indicator
  • OpenClaw logs show "Started streaming""dispatch complete""Closed streaming" with no errors
  • The card appears complete from OpenClaw's perspective
  • The user sees truncated content with no indication that data was lost

5. Non-streaming path comparison

When streaming: false:

  • Replies are sent via the text message path, not the card path
  • The chunker properly respects textChunkLimit (or configured value)
  • Text messages in Feishu have significantly higher content limits
  • Complete content is always delivered

Configuration that does NOT fix this

  • Changing textChunkLimit to a larger value has no effect on streaming mode — the value is only used by the chunker in non-streaming paths
  • The card maxLength in presentationCapabilities (4000) is also only checked in the non-streaming path

Workaround

Set channels.feishu.streaming: false to disable card streaming and fall back to plain-text message delivery:

{
  channels: {
    feishu: {
      streaming: false,
    },
  },
}

This restores the pre-v2026.5.30-beta.1 behavior where all plain-text replies are sent as Feishu text messages with no content truncation.

Suggested Fixes (upstream)

  1. Pre-card content length check: Before creating/updating a streaming card, check if the accumulated text exceeds a safe threshold (e.g., 8000 chars). If so, close the streaming card early and deliver the remainder as a plain text reply.

  2. Config-driven fallback: Add a config option like channels.feishu.renderMode with values "auto" (current default), "card" (always use cards), or "text" (always use text messages, pre-v2026.5.30-beta.1 behavior).

  3. Multi-card splitting: When content exceeds the card markdown limit, split across multiple sequential cards.

  4. Error detection: Monitor for silent truncation by comparing the length of text sent vs. the card's known maxLength limit, and log a warning when truncation is suspected.

Verification of Commit

SHA:       3b8ab4e11232
Author:    chuanchuan
Date:      2026-05-30 17:47:03 UTC
Message:   fix(feishu): stream plain replies as cards

Changed files:

  • extensions/feishu/src/reply-dispatcher.ts
  • extensions/feishu/src/reply-dispatcher.test.ts
  • extensions/feishu/src/streaming-card.ts

Log Evidence

Streaming events from an affected session (no errors, no truncation warnings):

[feishu] feishu[default] Started streaming: cardId=xxx
[feishu] feishu[default]: dispatch complete (queuedFinal=true, replies=1)
[feishu] feishu[default] Closed streaming: cardId=xxx

Related

  • Commit 3b8ab4e11232 (this report)
  • Commit d05e4a4bc6f2 fix(feishu): use full gateway channel runtime (related feishu channel dispatch fix)
  • channels.feishu.streaming config documentation in docs/channels/feishu.md

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 - 💡(How to fix) Fix [BUG] (feishu) Streaming cards silently truncate long plain-text replies due to Feishu card markdown content limits