Skip to content

fix: keep the taker message deliverable when MarkdownV2 parsing fails - #914

Open
ToRyVand wants to merge 2 commits into
lnp2pBot:mainfrom
ToRyVand:fix/issue-882-markdownv2-fallback
Open

fix: keep the taker message deliverable when MarkdownV2 parsing fails#914
ToRyVand wants to merge 2 commits into
lnp2pBot:mainfrom
ToRyVand:fix/issue-882-markdownv2-fallback

Conversation

@ToRyVand

@ToRyVand ToRyVand commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #882.

When a translation contains an unescaped MarkdownV2 reserved character, Telegram rejects the you_took_someone_order send. Because beginTakeSellMessage sends the order's Continue/Cancel buttons right after, the throw was caught by the outer handler and the buttons were never sent — so the taker was left not only without the confirmation text, but without any way to advance or cancel the order until it expired. That's the part worth highlighting: the impact is a stuck trade, not just a missing message.

Approach

The send now goes through a small helper that retries once without parse_mode when Telegram returns 400 can't parse entities, so the flow continues and the buttons are delivered.

  • Narrowed to parse errors. Any other failure (429, 403, network) is re-thrown untouched — retrying those could duplicate a message that was already delivered, ignore retry_after, or repeat a call guaranteed to fail. This follows the existing error instanceof TelegramError && error.response.error_code === … pattern in this file.
  • Escapes stripped on the retry. The locales carry \. / \( escapes that are meaningless without parse_mode; sending verbatim would show stray backslashes in the funds-risk warning.
  • The failing locale is logged, so the offending translation can be corrected at the source.

This complements #872 (escaping the Farsi locale) rather than replacing it: escaping fixes the known characters, this keeps the flow usable if a new one slips through.

Note (not addressed here)

onGoingTakeSellMessage has the same shape — a MarkdownV2 send sitting before the seller's "your order was taken" notification. It's latent today (/fiatsent is safe in all ten locales), so I left it out to keep this scoped to the reported bug. Happy to apply the same helper there if you'd prefer.

Local validation

npx tsc            ✅
npm run lint       ✅
npm run format     ✅
npm test           ✅ 217 passing

Tests cover the plain-text retry, that the action buttons are still delivered, that non-formatting errors are not retried, and that MarkdownV2 is preserved when the locale is safe. Verified the regression test fails without the fix (only 1 send happens; the buttons never go out).

Summary by CodeRabbit

  • Bug Fixes

    • Improved message delivery when Markdown formatting errors occur.
    • Automatically retries affected messages as plain text, allowing follow-up action buttons to remain available.
    • Non-formatting errors continue to be handled normally.
  • Tests

    • Added coverage for formatting failures, non-formatting errors, and locales where Markdown formatting succeeds.

A single unescaped MarkdownV2 reserved character in a translation makes
Telegram reject the send. In beginTakeSellMessage that send is followed by
the message carrying the order's Continue/Cancel buttons, so the throw was
caught by the outer handler and the buttons were never sent: the taker was
left both without the confirmation text and without any way to advance or
cancel the order until it expired.

Send the message through a helper that retries once without parse_mode when
Telegram rejects the formatting, and log the offending locale so the
translation can be corrected. Escaping locales (as in lnp2pBot#872) fixes the known
characters; this keeps the flow usable when a new one slips through.

Adds a regression test asserting the plain-text retry and, most importantly,
that the action buttons are still delivered.

Closes lnp2pBot#882
Two refinements to the MarkdownV2 fallback:

- The retry re-sent the MarkdownV2 source verbatim, so the reader saw the
  now-meaningless backslash escapes the locales carry (`\.`, `\(`) right in
  the funds-risk warning. Strip them before sending as plain text.
- The catch retried on any error. A network failure may mean the message was
  already delivered (duplicating a long warning), a 429 retry ignores
  retry_after, and a 403 retry is guaranteed to fail. Narrow the fallback to
  Telegram's 400 "can't parse entities" and re-throw everything else so the
  existing handler deals with it as before.

Extends the spec with the escape-stripping assertion and a case proving
non-formatting errors are not retried.
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ffcb858b-c040-4ed7-8e0a-acdaf492209f

📥 Commits

Reviewing files that changed from the base of the PR and between 1ff500b and d16d6e8.

📒 Files selected for processing (2)
  • bot/messages.ts
  • tests/bot/markdownV2Fallback.spec.ts

Walkthrough

The buyer notification flow now retries Telegram MarkdownV2 parse failures as plain text after removing escapes. Other errors remain unchanged. Tests cover fallback retries, action buttons, non-formatting errors, and safe localized messages.

Changes

MarkdownV2 fallback

Layer / File(s) Summary
Fallback sender and buyer-flow wiring
bot/messages.ts
MarkdownV2 parse errors trigger a logged plain-text retry. Other errors are re-thrown. beginTakeSellMessage uses the fallback-aware sender.
Fallback behavior tests
tests/bot/markdownV2Fallback.spec.ts
Tests verify retry behavior, action-button delivery, non-formatting errors, and safe MarkdownV2 messages.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • lnp2pBot/bot#872: Both changes address MarkdownV2 formatting failures in the buyer-take flow and Farsi locale strings.

Suggested reviewers: luquitasjeffrey, matobi98

Poem

A rabbit found a broken mark,
And sent plain words into the dark.
The buttons stayed beside the note,
While escaped signs learned how to float.
“Parse again!” the rabbit cheered.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary fix: preserving taker message delivery when MarkdownV2 parsing fails.
Linked Issues check ✅ Passed The changes satisfy issue #882 by adding a MarkdownV2 parse-error fallback, preserving safe formatting, and allowing action-button delivery.
Out of Scope Changes check ✅ Passed The production and test changes are limited to the MarkdownV2 fallback behavior and its regression coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Persian taker message not delivered due to Telegram MarkdownV2 parse error — proposed fallback fix

1 participant