Skip to content

fix(feishu): remove streaming card update throttle to prevent duplicates#66148

Closed
SARAMALI15792 wants to merge 1 commit into
openclaw:mainfrom
SARAMALI15792:fix/65993-feishu-streaming-throttle
Closed

fix(feishu): remove streaming card update throttle to prevent duplicates#66148
SARAMALI15792 wants to merge 1 commit into
openclaw:mainfrom
SARAMALI15792:fix/65993-feishu-streaming-throttle

Conversation

@SARAMALI15792

Copy link
Copy Markdown
Contributor

What does this PR do?

Removes artificial 100ms throttling from Feishu streaming card updates to eliminate duplicate and truncated messages when handling long replies.

Fixes #65993

Problem Statement

Feishu streaming cards produce duplicate or truncated messages when handling long replies (>1000 characters).

Root cause: The updateThrottleMs = 100 throttle in FeishuStreamingSession causes updates to be dropped when content blocks arrive faster than 100ms intervals. When close() is called with accumulated unsent text (pendingText), the content is truncated. Then the fallback sendChunkedTextReply sends the full content, resulting in duplicate messages.

User impact:

  • Case A (light): One streaming card + one additional non-streaming block with partial content
  • Case B (severe): Multiple message blocks with content from early blocks lost or duplicated

Why Telegram/Discord unaffected: They use editMessageText on a draft message without throttling. Feishu uses throttled card updates via FeishuStreamingSession.

Solution Applied

Changed updateThrottleMs from 100 to 0 in extensions/feishu/src/streaming-card.ts:172.

Why this solves the bottleneck:

  • Eliminates artificial delay that causes update drops
  • Feishu Card Kit API is designed for rapid updates (configured with print_frequency_ms: 50)
  • No updates skipped = no content loss = no truncation = no duplicates
  • Streaming cards now update immediately as content arrives

Why this is safe:

  • User validated 0ms throttle in production with no issues
  • Feishu's server-side rate limiting handles any excessive requests
  • Telegram/Discord successfully use 0ms throttle for streaming updates
  • No breaking changes - existing behavior improves

Changes

Modified:

  • extensions/feishu/src/streaming-card.ts:172 - Changed updateThrottleMs from 100 to 0

Testing:

  • ✅ All existing tests pass (7/7)
  • ✅ Type check passes
  • ✅ User validated in production

Test Plan

Automated:

  • Existing test suite passes
  • TypeScript compilation succeeds

Manual verification (for maintainers):

  1. Configure Feishu channel with streaming: true and renderMode: "card"
  2. Send request generating long reply (>1000 chars)
  3. Observe output in Feishu app
  4. Verify: Single streaming card with smooth incremental updates
  5. Verify: No duplicate message blocks
  6. Verify: No truncated content

Expected behavior:

  • Single streaming card message
  • Smooth incremental updates without 100ms delays
  • Complete final message
  • No duplicates or truncation

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: feishu Channel integration: feishu size: XS labels Apr 13, 2026
@greptile-apps

greptile-apps Bot commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Sets updateThrottleMs to 0 in FeishuStreamingSession, disabling the 100ms card-update throttle that was causing content to accumulate in pendingText without a scheduled flush timer, leading to truncated or duplicate messages on long replies.

  • The flushTimer field is declared and cleared but never assigned — the flush mechanism was always incomplete. Removing throttling is the right short-term fix, but the dead timer field should be cleaned up to avoid misleading future contributors.
  • docs/plans/2026-04-13-feishu-streaming-throttle-fix.md and docs/plans/2026-04-13-feishu-streaming-throttle-fix-spec.md are AI agent planning artifacts (agentic worker task checklists) that don't belong in the docs/ tree and should be dropped from this PR.

Confidence Score: 5/5

Safe to merge after dropping the docs/plans files; the one-line code change is correct and low-risk.

The substantive change is a single property assignment that correctly disables a throttle mechanism whose flush-timer side was never implemented. All remaining findings are P2: a dead flushTimer field worth cleaning up, and two AI planning documents that shouldn't live in docs/. Neither blocks correctness or safety.

docs/plans/2026-04-13-feishu-streaming-throttle-fix.md and docs/plans/2026-04-13-feishu-streaming-throttle-fix-spec.md should be removed before merging.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/feishu/src/streaming-card.ts
Line: 169-172

Comment:
**`flushTimer` is never set**

`flushTimer` is declared, cleared in both `update()` and `close()`, but never actually assigned a `setTimeout` call. The intent was clearly to schedule a deferred flush of `pendingText` after the throttle window expires — without it, any `pendingText` accumulated during a throttle window is only drained when the *next* non-throttled update arrives or when `close()` fires. Setting `updateThrottleMs = 0` avoids this gap entirely, but the dead timer field will confuse anyone who later tries to re-enable throttling (they'll add `= 0` back, the bug reappears, and the timer wiring is still missing). Consider removing `flushTimer` entirely now that throttling is disabled, or add the missing timer assignment if throttling is ever re-enabled.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: docs/plans/2026-04-13-feishu-streaming-throttle-fix.md
Line: 1-5

Comment:
**AI planning artifacts should not be committed**

Both `docs/plans/2026-04-13-feishu-streaming-throttle-fix.md` and `docs/plans/2026-04-13-feishu-streaming-throttle-fix-spec.md` are agentic-worker planning documents (note the `- [ ]` task syntax and "REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development" directive). These are internal scaffolding files, not user-facing documentation. The `docs/` directory is for user-facing content; committing agent-generated planning artifacts alongside code changes adds permanent noise to the repo history and will confuse contributors browsing the docs tree.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(feishu): remove streaming card updat..." | Re-trigger Greptile

Comment on lines 169 to +172
private lastUpdateTime = 0;
private pendingText: string | null = null;
private flushTimer: ReturnType<typeof setTimeout> | null = null;
private updateThrottleMs = 100; // Throttle updates to max 10/sec
private updateThrottleMs = 0; // No throttling - Feishu API handles rapid updates

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 flushTimer is never set

flushTimer is declared, cleared in both update() and close(), but never actually assigned a setTimeout call. The intent was clearly to schedule a deferred flush of pendingText after the throttle window expires — without it, any pendingText accumulated during a throttle window is only drained when the next non-throttled update arrives or when close() fires. Setting updateThrottleMs = 0 avoids this gap entirely, but the dead timer field will confuse anyone who later tries to re-enable throttling (they'll add = 0 back, the bug reappears, and the timer wiring is still missing). Consider removing flushTimer entirely now that throttling is disabled, or add the missing timer assignment if throttling is ever re-enabled.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/streaming-card.ts
Line: 169-172

Comment:
**`flushTimer` is never set**

`flushTimer` is declared, cleared in both `update()` and `close()`, but never actually assigned a `setTimeout` call. The intent was clearly to schedule a deferred flush of `pendingText` after the throttle window expires — without it, any `pendingText` accumulated during a throttle window is only drained when the *next* non-throttled update arrives or when `close()` fires. Setting `updateThrottleMs = 0` avoids this gap entirely, but the dead timer field will confuse anyone who later tries to re-enable throttling (they'll add `= 0` back, the bug reappears, and the timer wiring is still missing). Consider removing `flushTimer` entirely now that throttling is disabled, or add the missing timer assignment if throttling is ever re-enabled.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +1 to +5
# Feishu Streaming Card Throttle Fix Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Remove artificial throttling from Feishu streaming card updates to eliminate duplicate/truncated messages

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 AI planning artifacts should not be committed

Both docs/plans/2026-04-13-feishu-streaming-throttle-fix.md and docs/plans/2026-04-13-feishu-streaming-throttle-fix-spec.md are agentic-worker planning documents (note the - [ ] task syntax and "REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development" directive). These are internal scaffolding files, not user-facing documentation. The docs/ directory is for user-facing content; committing agent-generated planning artifacts alongside code changes adds permanent noise to the repo history and will confuse contributors browsing the docs tree.

Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/plans/2026-04-13-feishu-streaming-throttle-fix.md
Line: 1-5

Comment:
**AI planning artifacts should not be committed**

Both `docs/plans/2026-04-13-feishu-streaming-throttle-fix.md` and `docs/plans/2026-04-13-feishu-streaming-throttle-fix-spec.md` are agentic-worker planning documents (note the `- [ ]` task syntax and "REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development" directive). These are internal scaffolding files, not user-facing documentation. The `docs/` directory is for user-facing content; committing agent-generated planning artifacts alongside code changes adds permanent noise to the repo history and will confuse contributors browsing the docs tree.

How can I resolve this? If you propose a fix, please make it concise.

@openclaw-barnacle openclaw-barnacle Bot added channel: discord Channel integration: discord gateway Gateway runtime extensions: memory-core Extension: memory-core agents Agent runtime and tooling size: S and removed size: XS labels Apr 13, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a58b1c6c56

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@@ -169,7 +169,7 @@ export class FeishuStreamingSession {
private lastUpdateTime = 0;
private pendingText: string | null = null;
private flushTimer: ReturnType<typeof setTimeout> | null = null;
private updateThrottleMs = 100; // Throttle updates to max 10/sec
private updateThrottleMs = 0; // No throttling - Feishu API handles rapid updates

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep a positive streaming update throttle

Setting updateThrottleMs to 0 makes the throttle guard in update() effectively unreachable, so every queued partial snapshot now performs a full CardKit PUT instead of coalescing updates. In the Feishu reply path, partialUpdateQueue and close() both await these updates, so long responses with many small deltas can accumulate hundreds/thousands of sequential network calls and significantly delay completion (and are much more likely to hit Feishu-side rate limiting). This change removes the only backpressure/coalescing mechanism on this hot path.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b61d251337

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread docs/CLAUDE.md Outdated
AGENTS.md

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove trailing newline from CLAUDE.md symlink target

The symlink blob now stores AGENTS.md\n instead of AGENTS.md, so docs/CLAUDE.md resolves to a non-existent path and is effectively broken for tools/readers that follow it. This commit changed only the symlink payload, and adding the newline makes the target invalid even though docs/AGENTS.md exists, so consumers relying on docs/CLAUDE.md will fail to load the intended instructions.

Useful? React with 👍 / 👎.

Fixes openclaw#65993

The 100ms throttle was causing updates to be dropped when blocks arrived
faster than the throttle interval. This led to:
- Lost content during streaming
- Truncated messages when close() was called with pending text
- Duplicate messages when fallback sendChunkedTextReply sent full content

Feishu Card Kit API is designed for rapid updates (print_frequency_ms: 50).
User validated 0ms throttle in production with no issues.

Also removes unused flushTimer field that was never assigned.
@SARAMALI15792 SARAMALI15792 force-pushed the fix/65993-feishu-streaming-throttle branch from 663f496 to 36ebf31 Compare April 13, 2026 20:22
@openclaw-barnacle openclaw-barnacle Bot added size: XS and removed docs Improvements or additions to documentation channel: discord Channel integration: discord gateway Gateway runtime extensions: memory-core Extension: memory-core agents Agent runtime and tooling size: S labels Apr 13, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36ebf3149e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

private pendingText: string | null = null;
private flushTimer: ReturnType<typeof setTimeout> | null = null;
private updateThrottleMs = 100; // Throttle updates to max 10/sec
private updateThrottleMs = 0; // No throttling - Feishu API handles rapid updates

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore coalescing for streaming card partial updates

Setting updateThrottleMs to 0 makes the throttle branch in FeishuStreamingSession.update() unreachable, so every partial snapshot now enqueues a full CardKit PUT instead of collapsing rapid updates. In the Feishu reply path, onPartialReply can emit many snapshots and closeStreaming() waits partialUpdateQueue before finalizing, so long replies will perform a large serial request chain, which materially increases completion latency and makes rate-limit/network failure much more likely under high-chunk streams.

Useful? React with 👍 / 👎.

@SARAMALI15792

Copy link
Copy Markdown
Contributor Author

CI Status Update

The failing CI checks are not related to this Feishu fix.

This PR:

  • ✅ Only modifies extensions/feishu/src/streaming-card.ts
  • ✅ All Feishu tests pass (7/7)
  • ✅ Local TypeScript check passes
  • ✅ Single focused change: remove 100ms throttle + dead flushTimer cleanup

CI Failures:
The TypeScript errors are in completely unrelated extensions:

  • Discord (inbound-dedupe.ts, message-handler.ts)
  • Telegram (bot-message-context.ts, bot-message-dispatch.ts)
  • WhatsApp (monitor.ts)
  • Nextcloud Talk (monitor.replay.test.ts)
  • And others

These appear to be pre-existing issues from recent main branch changes, not introduced by this PR.

Recommendation: This Feishu fix can be merged independently as it's isolated and working correctly.

@Dany-Deng-cn

Copy link
Copy Markdown

🎉 Thank you!

Thank you @SARAMALI15792 for the fix! This resolves the Feishu streaming card duplicate/truncation issue we reported in #65993.

Your analysis confirmed our root cause finding (the 100ms throttle in ). The fix looks clean and minimal - exactly what's needed.

We're currently running with a dist-file patch () as a temporary workaround, so we'll be happy to remove it once this PR is merged! ✅

@prtags

prtags Bot commented Apr 23, 2026

Copy link
Copy Markdown

Related work from PRtags group moving-rooster-bp2l

Title: Open PR candidate: Feishu streaming duplicate messages

Number Title
#66148* fix(feishu): remove streaming card update throttle to prevent duplicates
#68491 fix(feishu): prevent duplicate message after streaming card close (#67791)

* This PR

@vincentkoc vincentkoc added dedupe:child Duplicate issue/PR child in dedupe cluster close:superseded PR close reason labels Apr 25, 2026
@vincentkoc vincentkoc self-assigned this Apr 25, 2026
@vincentkoc

Copy link
Copy Markdown
Member

Thanks for chasing this.

I’m closing this as superseded. The reporter later confirmed the throttle change did not address the root cause. The canonical remaining fix is #68491, which handles the onIdle-before-final duplicate-card race in the Feishu dispatcher and now includes the missing regression test: #68491

Your investigation helped narrow the failure, but this patch is no longer the merge-safe path. If a new throttle-specific regression shows up on current main, please open a fresh issue/PR with that narrower evidence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: feishu Channel integration: feishu close:superseded PR close reason dedupe:child Duplicate issue/PR child in dedupe cluster size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feishu streaming card produces duplicate/truncated messages with long replies

3 participants