Skip to content

fix(gateway/telegram): fallback to plain text on Markdown parse failure#946

Open
chaodu-agent wants to merge 4 commits into
mainfrom
fix/871-telegram-markdown-fallback
Open

fix(gateway/telegram): fallback to plain text on Markdown parse failure#946
chaodu-agent wants to merge 4 commits into
mainfrom
fix/871-telegram-markdown-fallback

Conversation

@chaodu-agent
Copy link
Copy Markdown
Collaborator

Summary

Closes #871.

When Telegram rejects a message due to Markdown parse errors (unescaped special characters in URLs, Chinese text, code snippets), the adapter now detects the API-level failure (ok: false) and retries without parse_mode.

Problem

Agent response (contains _ * [ ] `)
        │
        ▼
telegram.rs: let _ = client.post(sendMessage)
  parse_mode: "Markdown"
        │
        ▼
Telegram API: {"ok": false, "description": "can't parse entities"}
        │
        ▼
Message silently dropped — user sees ✅ reaction but no text

Two issues:

  1. let _ = discarded the HTTP response — Telegram API errors (ok: false) were never checked
  2. parse_mode: "Markdown" is strict — unescaped _, *, [, ], ``` cause rejection

Fix

Try with Markdown first. If Telegram returns ok: false, log a warning and retry as plain text (no parse_mode). Network errors are still logged without retry.

Changes

File Change
gateway/src/adapters/telegram.rs Check sendMessage response body; retry without parse_mode on failure

Validation

  • cargo check — no C linker in agent environment; CI will verify
  • Code review: pattern matches existing gateway error handling style
  • Minimal change: +32/-2 lines, single file

Discord Discussion URL: https://discord.com/channels/1491295327620169908/1504195583676780605

Closes #871.

When Telegram rejects a message due to Markdown parse errors (unescaped
special characters in URLs, Chinese text, code), the adapter now detects
the API-level failure (ok: false) and retries without parse_mode.

Previously, `let _ =` discarded the response entirely, causing messages
to silently disappear while the bot still showed success reactions.
@chaodu-agent chaodu-agent requested a review from thepagent as a code owner May 28, 2026 11:14
超渡法師 added 2 commits May 28, 2026 11:15
Address review findings:
- F1: Only retry as plain text when description contains 'parse' or
  'entities'. Other errors (429 rate limit, 403 forbidden) are logged
  but not retried to avoid wasting API calls.
- F2: Plain text fallback now checks response and logs error if it
  also fails, improving observability.
Address 普渡法師 review:
- F2: Replace fragile desc.contains() string matching with
  error_code == 400 check. Telegram 400 = Bad Request (parse errors);
  429/403/etc are not retried.
- F3: Handle JSON decode failure explicitly with warn log instead of
  unwrap_or_default() which could silently trigger false retries.
- F1 (MarkdownV2): Intentionally deferred — switching parse_mode is a
  behavior change beyond this bug fix scope. MarkdownV2 requires more
  escaping and would cause more failures without a full escape pipeline.
- F4 (tests): No test framework exists for gateway telegram adapter.
  Tracked as follow-up.
@shaun-agent
Copy link
Copy Markdown
Contributor

OpenAB PR Screening

This is auto-generated by the OpenAB project-screening flow for context collection and reviewer handoff.
Click 👍 if you find this useful. Human review will be done within 24 hours. We appreciate your support and contribution 🙏

Screening report blocked at the write step. i verified with `gh` that:

then the command sandbox started failing even for true / gh --version with:

bwrap: No permissions to create new namespace

so i could not post the GitHub comment or move the project item. i’m not going to pretend that happened.

screening report prepared:

<!-- openab-project-screening -->

## Intent

Fix Telegram message delivery failures where Telegram accepts the HTTP request but rejects Markdown entity parsing at the API layer. The operator-visible symptom is misleading success handling: users can see a success reaction while the actual Telegram reply is silently dropped.

## Feat

Fix work. The Telegram gateway adapter now attempts `sendMessage` with Markdown first, inspects the Telegram API response, and retries as plain text without `parse_mode` when Telegram returns an API-level failure such as `can't parse entities`.

## Who It Serves

Telegram users and gateway runtime operators. Users get the message instead of a false-success drop; operators get warning logs instead of a swallowed API response.

## Rewritten Prompt

Update `gateway/src/adapters/telegram.rs` so outbound Telegram text messages are not silently lost when Markdown parsing fails. Send with the existing Markdown behavior first, parse the Telegram API response body, and if the response reports `ok: false`, log the failure and retry the same message as plain text without `parse_mode`. Preserve existing network-error handling, avoid retry loops, and add or keep focused validation around the response parsing path where practical.

## Merge Pitch

This should move forward because it addresses a real delivery correctness bug with a narrow one-file change. The risk profile is low: the fallback only triggers after Telegram rejects the formatted send, and the second attempt trades formatting for delivery. The main reviewer concern is likely whether all `ok: false` responses should fall back to plain text or whether retry should be limited to parse/entity errors to avoid masking unrelated Telegram API failures.

## Best-Practice Comparison

OpenClaw's durable delivery model would treat this as an explicit delivery failure with retry/backoff and run logs; this PR is a smaller adapter-local version of that idea, improving response inspection and retry behavior without adding job persistence. Hermes Agent's scheduling and persisted prompt model does not directly apply because this is synchronous outbound message delivery, not scheduled execution. The relevant overlap is explicit failure handling: do not discard API responses when they determine user-visible delivery.

## Implementation Options

Conservative: accept this PR's adapter-local fallback as-is, with reviewer attention on response parsing and warning quality. This is fastest and fixes the user-visible drop.

Balanced: restrict fallback to Telegram parse/entity failures, log non-parse `ok: false` responses as hard send failures, and add a focused unit test or helper-level test for Markdown failure response handling. This keeps delivery resilient without hiding unrelated API errors.

Ambitious: introduce a small shared gateway send-result abstraction for outbound adapters, so API-level failures, retries, and delivery logs are represented consistently across Telegram and future adapters. This is more maintainable long term but larger than the immediate bug requires.

## Comparison Table

| Option | Speed | Complexity | Reliability | Maintainability | User Impact | Fit for OpenAB now |
|---|---:|---:|---:|---:|---:|---|
| Conservative | High | Low | Medium | Medium | High | Good for quick unblock |
| Balanced | Medium | Low-Medium | High | High | High | Best fit |
| Ambitious | Low | High | High | High | High | Better as follow-up |

## Recommendation

Proceed with the balanced path if review can request a small refinement: fallback only for Telegram Markdown/entity parse failures and keep other `ok: false` responses visible as send failures. If the current implementation already cleanly exposes the failed response and avoids loops, this is suitable for Masami or Pahud follow-up review as a narrow correctness fix. Broader delivery persistence and adapter-wide retry semantics should be split out.

Agent-ran OpenAB PR screening. Feedback welcome; react thumbs-up if useful.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Telegram sendMessage silently fails on Markdown parse errors

3 participants