Skip to content

issue(telegram): persistent rich messages collapse ordinary line breaks #95409

Description

@snowzlmbot

Problem

Telegram richMessages final replies can collapse ordinary line breaks into a single paragraph, making structured assistant replies hard to read even though the same text renders correctly through the ordinary sendMessage path.

This affects the supported account-scoped persistent rich-message path, not the rejected sendRichMessageDraft streaming design.

Latest main checked

  • Repository: openclaw/openclaw
  • Base checked: latest upstream/main at 97b0b559ad17b6f567fc195d26c610b931f52d3b

Reproduction shape

  1. Enable Telegram rich messages for the selected account:

    • channels.telegram.richMessages = true
    • channels.telegram.accounts.default.richMessages = true
  2. Send a normal final assistant reply with plain line-separated text, e.g.:

    第一行
    第二行
    第三行
    
    - 列表 1
    - 列表 2
    
  3. The ordinary sendMessage path preserves the line breaks, but the rich-message final path can render the same content visually compressed.

Source-level evidence

On current main, extensions/telegram/src/rich-message.ts prepares rich HTML with only sanitize + nesting-limit handling:

function prepareTelegramRichHtml(html: string): string {
  return limitTelegramRichHtmlNesting(sanitizeTelegramRichHtml(html), TELEGRAM_RICH_NESTING_LIMIT);
}

This means ordinary text newlines that survive Markdown/HTML conversion can be passed to rich_message.html as raw \n. Telegram Rich HTML rendering does not treat those raw newlines like plain sendMessage text; they need explicit <br> or block structure for reliable visual line breaks.

Observed behavior

In a real Telegram direct-chat run with richMessages enabled, final replies using the official Bot API 10.1 sendRichMessage path lost the expected visual line separation. A local candidate change that converts text-node newlines to <br> before rich-message send restored the expected formatting while still using sendRichMessage / rich_message.

The candidate behavior was verified by inspecting the outbound call shape:

  • operation: sendRichMessage
  • payload: rich_message.html
  • ordinary text newlines materialized as <br>

Expected behavior

Persistent Telegram rich final replies should preserve ordinary assistant line breaks at least as well as the non-rich sendMessage path.

The fix should:

  • stay on the existing account-scoped persistent rich-message path;
  • avoid sendRichMessageDraft entirely;
  • preserve text-node newlines as <br> or equivalent rich-message structure;
  • not insert <br> inside preformatted/code blocks;
  • keep richMessages: false fallback behavior unchanged.

Why this is separate from #93438

#93438 was closed because native sendRichMessageDraft conflicts with the established Telegram streaming contract. This issue is different: it targets only the currently supported persistent rich-message final/preview path (sendRichMessage and editMessageText with rich_message) and does not propose using native drafts for answer streaming.

Risk

Low-to-medium. The change is in rich HTML preparation for opted-in Telegram rich messages. It may affect rich HTML layout, so tests should cover:

  • plain text newline preservation;
  • no newline-to-<br> conversion inside <pre> / <code>;
  • existing <br> HTML remains unchanged;
  • rich-disabled path still uses normal send/edit fallback.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:needs-live-reproClawSweeper needs live local, crabbox, or manual validation to confirm this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.issue-rating: 🐚 platinum hermitGood issue quality with a plausible reproduction path needing some confirmation.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions