Skip to content

fix(telegram): chunk long html outbound messages#42240

Merged
obviyus merged 6 commits intomainfrom
codex/telegram-html-chunking
Mar 10, 2026
Merged

fix(telegram): chunk long html outbound messages#42240
obviyus merged 6 commits intomainfrom
codex/telegram-html-chunking

Conversation

@obviyus
Copy link
Copy Markdown
Contributor

@obviyus obviyus commented Mar 10, 2026

Summary

  • Problem: Telegram sends that go through the HTML-mode outbound path (channelData / inline buttons) bypass chunking and hit Telegram's 4096-char limit.
  • Why it matters: long replies and subagent completion announces can fail with 400: Bad Request: message is too long, silently dropping user-visible output.
  • What changed: added an HTML-aware chunker, routed HTML-mode text sends through it, and kept Telegram inline buttons on the last chunk only.
  • What did NOT change (scope boundary): markdown-mode plain-text fallback/retry behavior, non-Telegram channels, and normal short-message delivery.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • 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

User-visible / Behavior Changes

Long Telegram HTML-mode replies now arrive as multiple messages instead of failing once they exceed Telegram's message limit. Inline buttons remain attached to the final chunk.

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
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local Node/Bun test harness
  • Model/provider: N/A
  • Integration/channel (if any): Telegram
  • Relevant config (redacted): default Telegram send path with inline buttons

Steps

  1. Call telegramOutbound.sendPayload with payload.text longer than 4096 chars and channelData.telegram.buttons present.
  2. Let that route into sendMessageTelegram.
  3. Observe the Bot API calls.

Expected

  • Multiple sendMessage calls, each within Telegram's limit.
  • reply_markup only on the last chunk.

Actual

  • Before fix: one oversized sendMessage call, then 400: Bad Request: message is too long.
  • After fix: two sends in local repro (4000 and 1001 chars) with buttons only on the last chunk.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: reproduced the old failure locally, added a regression test for long HTML-mode sends with buttons, confirmed the same repro now emits two Telegram sends with buttons only on the last chunk.
  • Edge cases checked: long HTML-mode send, HTML tag balancing across chunks, long follow-up text after caption split, existing HTML parse fallback behavior.
  • What you did not verify: live Telegram delivery against a real bot token; full src/telegram/send.test.ts still has two pre-existing retry-test failures unrelated to this 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
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: revert this PR.
  • Files/config to restore: src/telegram/send.ts, src/telegram/format.ts
  • Known bad symptoms reviewers should watch for: malformed HTML in chunked Telegram replies, buttons appearing on every chunk, or long HTML-mode sends still failing with message is too long.

Risks and Mitigations

  • Risk: HTML chunking could split malformed caller-provided HTML awkwardly.
    • Mitigation: chunker preserves open-tag context and closes/reopens balanced tags per chunk; coverage added for long tagged input.

@openclaw-barnacle openclaw-barnacle Bot added the channel: telegram Channel integration: telegram label Mar 10, 2026
@openclaw-barnacle openclaw-barnacle Bot added size: M maintainer Maintainer-authored PR labels Mar 10, 2026
@aisle-research-bot

This comment was marked as outdated.

@obviyus obviyus self-assigned this Mar 10, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 10, 2026

Greptile Summary

This PR correctly fixes the core bug—Telegram messages via the HTML-mode outbound path now chunk properly instead of hitting the 4096-char limit. The splitTelegramHtmlChunks function is well-implemented with tag balancing and entity-boundary guards, and routing is sound.

One logic issue: The overflow branch in splitTelegramPlainTextFallback (lines 119–125) can produce more chunks than the HTML chunker, causing the tail of opts.plainText to be silently dropped when plain text exceeds chunkCount * limit. The proportional branch below handles this correctly and should be used unconditionally. This is unlikely to trigger in common cases (plain text must be significantly longer than all HTML chunks combined), but it is a data-loss correctness hole when it does.

Confidence Score: 4/5

  • Safe to merge with one moderate logic issue in plain-text fallback overflow handling that requires an edge-case caller configuration to trigger.
  • The core HTML chunking and routing logic is well-implemented and correctly fixes the reported bug. The HTML chunker preserves tag balance, guards against entity splits, and errors loudly on impossible edge cases. The one logic issue (overflow branch in splitTelegramPlainTextFallback producing more chunks than expected) is a real correctness hole but requires plain text to be significantly longer than all HTML chunks combined—an unusual configuration in practice. The proportional fallback path already exists and works correctly, making the fix straightforward.
  • src/telegram/send.ts (lines 119–125): The splitTelegramPlainTextFallback overflow branch should be removed in favor of the proportional logic that always produces exactly chunkCount chunks.

Last reviewed commit: 1856ec9

Comment thread src/telegram/send.ts Outdated
Comment thread src/telegram/format.ts
Comment thread src/telegram/format.ts
Comment thread src/telegram/format.test.ts Outdated
Copy link
Copy Markdown

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

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: e264a8b724

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/telegram/send.ts Outdated
@obviyus
Copy link
Copy Markdown
Contributor Author

obviyus commented Mar 10, 2026

@greptile-apps fix, review again.

Copy link
Copy Markdown

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

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: 1856ec91f2

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/telegram/format.ts
Comment thread src/telegram/send.ts Outdated
Comment thread src/telegram/send.ts Outdated
Copy link
Copy Markdown

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

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: 6eee849ad9

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/telegram/send.ts
const plainTextChunks = splitTelegramPlainTextFallback(fallbackText, htmlChunks.length, 4000);
return htmlChunks.map((htmlText, index) => ({
htmlText,
plainText: plainTextChunks[index] ?? htmlText,
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 Treat empty fallback slices as missing in chunked HTML retries

When opts.plainText is shorter than the number of HTML chunks, splitTelegramPlainTextFallback can emit empty strings for trailing chunks, and this mapping keeps them because ?? only falls back on null/undefined. If a later HTML chunk hits a parse error, requestPlain then retries with "", which Telegram rejects with message text is empty, so the send fails instead of recovering. This is introduced in the chunked HTML path and is reproducible with a long malformed HTML message plus a short plainText fallback.

Useful? React with 👍 / 👎.

obviyus added a commit that referenced this pull request Mar 10, 2026
@obviyus obviyus force-pushed the codex/telegram-html-chunking branch from 6eee849 to dd8d85a Compare March 10, 2026 15:49
Copy link
Copy Markdown

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

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: dd8d85abb0

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/telegram/send.ts
const plainTextChunks = splitTelegramPlainTextFallback(fallbackText, htmlChunks.length, 4000);
return htmlChunks.map((htmlText, index) => ({
htmlText,
plainText: plainTextChunks[index] ?? htmlText,
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 Treat empty fallback chunks as missing text

When opts.plainText is shorter than the number of HTML chunks, splitTelegramPlainTextFallback can emit "" for trailing entries, and this mapping preserves those empty strings because it uses ??. If a later HTML chunk hits a parse-entity error, the retry path sends an empty plain-text message for that chunk, which Telegram rejects (message text is empty), so delivery fails instead of falling back to the chunk content.

Useful? React with 👍 / 👎.

@obviyus obviyus force-pushed the codex/telegram-html-chunking branch from dd8d85a to 4d79c41 Compare March 10, 2026 17:22
@obviyus obviyus merged commit 3b582f1 into main Mar 10, 2026
27 checks passed
@obviyus obviyus deleted the codex/telegram-html-chunking branch March 10, 2026 17:23
frankekn pushed a commit to MoerAI/openclaw that referenced this pull request Mar 11, 2026
Merged via squash.

Prepared head SHA: 4d79c41
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
frankekn pushed a commit to Effet/openclaw that referenced this pull request Mar 11, 2026
Merged via squash.

Prepared head SHA: 4d79c41
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
frankekn pushed a commit to ImLukeF/openclaw that referenced this pull request Mar 11, 2026
Merged via squash.

Prepared head SHA: 4d79c41
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
Treedy2020 pushed a commit to Treedy2020/openclaw that referenced this pull request Mar 11, 2026
Merged via squash.

Prepared head SHA: 4d79c41
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
Ruijie-Ysp pushed a commit to Ruijie-Ysp/clawdbot that referenced this pull request Mar 12, 2026
Merged via squash.

Prepared head SHA: 4d79c41
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
leozhengliu-pixel pushed a commit to leozhengliu-pixel/openclaw that referenced this pull request Mar 13, 2026
Merged via squash.

Prepared head SHA: 4d79c41
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
Merged via squash.

Prepared head SHA: 4d79c41
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
Merged via squash.

Prepared head SHA: 4d79c41
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
Merged via squash.

Prepared head SHA: 4d79c41
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram maintainer Maintainer-authored PR size: L

Projects

None yet

1 participant