| name | deliver-format-fallback |
| description | Use when sending markdown/rich-text formatted messages through a strict wire-format channel API (Telegram, Slack, Discord, SMS/WhatsApp). Triggers — hardening a bot reply path, a message that "sometimes doesn't arrive", or literal *asterisks*/backticks showing in delivered messages. On any parse/entity 4xx, retry with a degraded payload so a malformed render never silently drops a reply on a critical notification channel. |
Deliver-Format Fallback
Overview
Strict-wire-format channel APIs (Telegram parse_mode, Slack mrkdwn, Discord markdown) reject a message whose formatting doesn't parse (unbalanced *, an unescaped _ in an identifier). If you treat that 4xx as a hard failure, the reply is dropped whole on a critical notification channel. Degrade the payload, don't drop it.
When to use
- Rendering markdown/rich text into a channel send (
sendMessage with parse_mode, Slack blocks, Discord embeds).
- A reply "sometimes doesn't arrive," or delivered messages show literal
*asterisks*/backticks.
The core rule
On the channel's SPECIFIC parse/entity-error signature, retry with a more-degraded representation — most-faithful-first — and only treat it as a real failure after the plain-text attempt.
def send_rich(client, base_payload, raw_text):
resp = client.post(url, json={**base_payload, "text": render(raw_text), "parse_mode": "HTML"})
if resp.status_code < 400:
return resp
return client.post(url, json={**base_payload, "text": raw_text})
Escalation chain: (1) fully formatted → (2) re-escape reserved chars, retry once → (3) strip parse_mode/formatting, resend raw → only THEN a real send failure feeding your durable-delivery path.
Guardrails
- Detect the SPECIFIC signature (Telegram:
400 + description can't parse entities). A 403 (permissions) or 429 (rate-limit) needs backoff/alert, NOT a format downgrade.
- Cap at ~2 retries and make each idempotent/replacing (edit-in-place or a single terminal send) — never an infinite or duplicate-send loop.
- Where the platform supports an entities-array (offset/length spans, Telegram), prefer it over
parse_mode string-markup — it sidesteps the whole escaping class deterministically.
Common mistakes
- Treating every 4xx as fatal → drop a message that a plain-text resend would have delivered.
- Retrying the SAME formatted payload → same parse error, still dropped.
- A naive
parse_mode flip with no fallback → 400s on the _ in identifiers (my_user_name) and drops the message.
Real-world impact
A strict-format messaging API needs this discipline: a render + retry-plain-on-4xx fix renders light markdown but NEVER drops on a malformed entity. This is the send-time partner to channel-adapter-pattern (design-time medium threading) and deliver-durably (crash-survivable outbox). Exa-confirmed against multiple production bots on strict-wire-format channels implementing the exact escape → re-escape → strip-and-resend-plain chain.