openclaw - ✅(Solved) Fix [Bug]: Block streaming splits markdown tables into separate Telegram messages [1 pull requests, 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#66614Fetched 2026-04-15 06:25:22
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
0
Timeline (top)
commented ×1cross-referenced ×1mentioned ×1subscribed ×1

Block streaming with flushOnParagraph splits at \n\n paragraph boundaries inside and around markdown tables, causing a single reply containing text + table + text to arrive as 3 separate Telegram messages.

Root Cause

Related to #47454, #17679. Root cause is that parseFenceSpans protects code blocks from splitting but no equivalent protection exists for markdown tables.

Fix Action

Fixed

PR fix notes

PR #66568: fix(streaming): prevent block chunker from splitting markdown tables

Description (problem / solution / changelog)

Summary

  • Problem: Block streaming (flushOnParagraph) treats \n\n around markdown tables as valid split points, splitting tables across multiple Telegram messages.
  • Why it matters: Tables are unreadable when fragmented across messages.
  • What changed: Added parseTableSpans() / isSafeTableBreak() mirroring the existing FenceSpan pattern, wired into all break-finding paths in EmbeddedBlockChunker. Moved table span detection to standalone table-spans.ts to avoid import cycle.
  • What did NOT change: No changes to deliver.ts, config resolution, or non-table chunking behavior.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #66614
  • Related #47454
  • Related #17679
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: findNextParagraphBreak() and all findSafe*BreakIndex() functions only checked isSafeFenceBreak(). Tables have no equivalent protection.
  • Missing detection / guardrail: No table span detection existed.
  • Contributing context: The fence-protection pattern was correct and extensible — tables just were not covered.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: pi-embedded-block-chunker.test.ts, markdown/tables.test.ts
  • Scenario the test should lock in: text+table+text under maxChars → single chunk; table stays intact when flushOnParagraph splits around it; thematic breaks not falsely detected as tables
  • Why this is the smallest reliable guardrail: Unit tests on the chunker directly verify the break-finding logic without needing a full channel roundtrip.
  • Existing test that already covers this: None — new tests added in this PR.
  • If no new test is added, why not: N/A — new tests added.

User-visible / Behavior Changes

Markdown tables in streaming replies are no longer split into separate messages.

Diagram (if applicable)

Before:
[text] → msg1 | [table] → msg2 | [text] → msg3

After:
[text + table + text] → msg1

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: macOS 26.4.1 (arm64)
  • Runtime/container: Node 22+ / pnpm global
  • Model/provider: anthropic/claude-sonnet-4
  • Integration/channel: Telegram (block streaming + flushOnParagraph)
  • Relevant config: default streaming config

Steps

  1. Send a prompt that triggers a response containing text, a markdown table, then more text
  2. Observe the Telegram delivery

Expected

  • One message containing the full response with the table inline

Actual

  • Three separate messages: text before table, the table itself, text after table

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)
<img width="1126" height="565" alt="image" src="https://github.com/user-attachments/assets/8d2e115f-baf1-4f08-9fe0-2a20a8a3176f" />

Unit tests: 15+ new tests all passing. Live verified: applied patched build to local OpenClaw, sent table reply via Telegram, confirmed single message delivery.

Human Verification (required)

  • Verified scenarios: Unit tests + live Telegram end-to-end
  • Edge cases checked: Table inside code fence (ignored), thematic break --- (not falsely detected), table near maxChars
  • What you did not verify: Other channels (Slack, WhatsApp) — behavior should be identical since they share the same chunker code path.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: Very large tables exceeding maxChars will force-split at maxChars instead of \n\n.
    • Mitigation: Matches existing code fence behavior — correct fallback.

Changed files

  • src/agents/pi-embedded-block-chunker.test.ts (modified, +85/-4)
  • src/agents/pi-embedded-block-chunker.ts (modified, +30/-13)
  • src/auto-reply/chunk.ts (modified, +3/-1)
  • src/markdown/table-spans.ts (added, +116/-0)
  • src/markdown/tables.test.ts (modified, +47/-0)
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

Block streaming with flushOnParagraph splits at \n\n paragraph boundaries inside and around markdown tables, causing a single reply containing text + table + text to arrive as 3 separate Telegram messages.

Steps to reproduce

  1. Configure Telegram channel with streaming mode partial and block streaming enabled (default)
  2. Send a prompt that triggers a response containing text, a markdown table, then more text
  3. Observe the Telegram delivery

Expected behavior

One Telegram message containing the full response with the table inline.

Actual behavior

Three separate messages: text before table, the table itself, text after table. The \n\n surrounding the table is treated as a valid paragraph split point.

In some cases, table rows are partially split — e.g. the last cell value ("Gentle") leaks outside the code block while the rest of the table stays inside:

<img width="1150" height="685" alt="Image" src="https://github.com/user-attachments/assets/9e3ff136-9848-4c1f-b4af-49116545e29a" />

OpenClaw version

2026.4.14

Operating system

macOS 26.4.1 (arm64)

Install method

pnpm global

Model

anthropic/claude-sonnet-4

Provider / routing chain

openclaw -> anthropic

Logs, screenshots, and evidence

Confirmed by code analysis: EmbeddedBlockChunker.drain() in pi-embedded-block-chunker.ts calls findNextParagraphBreak() which only checks isSafeFenceBreak() — no table awareness exists.

Impact and severity

  • Affected: All channels using block streaming with flushOnParagraph (Telegram, etc.)
  • Severity: Annoying (breaks conversation flow, tables unreadable when fragmented)
  • Frequency: 100% when response contains markdown tables
  • Consequence: Tables split across messages, poor UX

Additional information

Related to #47454, #17679. Root cause is that parseFenceSpans protects code blocks from splitting but no equivalent protection exists for markdown tables.

Fix: #66568 Follow-up integration test: #66605

extent analysis

TL;DR

Modify the findNextParagraphBreak() function to include table awareness, preventing splits at \n\n paragraph boundaries inside and around markdown tables.

Guidance

  • Review the EmbeddedBlockChunker.drain() method in pi-embedded-block-chunker.ts to understand how paragraph breaks are currently detected.
  • Update the findNextParagraphBreak() function to check for markdown table boundaries, in addition to isSafeFenceBreak(), to prevent unwanted splits.
  • Consider adding a test case to verify that tables are no longer split across messages, as mentioned in follow-up integration test #66605.
  • Investigate the related issues #47454 and #17679 to ensure the fix addresses all relevant scenarios.

Example

No code example is provided, as the necessary changes depend on the specific implementation details of findNextParagraphBreak() and parseFenceSpans.

Notes

The fix may require modifications to the parseFenceSpans function to provide equivalent protection for markdown tables, as mentioned in the issue.

Recommendation

Apply workaround: Modify the findNextParagraphBreak() function to include table awareness, as this directly addresses the root cause of the issue and prevents unwanted splits.

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

One Telegram message containing the full response with the table inline.

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]: Block streaming splits markdown tables into separate Telegram messages [1 pull requests, 1 comments, 2 participants]