openclaw - ✅(Solved) Fix Telegram retry regex misses grammY HttpError format — retries never fire for network errors [5 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#51525Fetched 2026-04-08 01:10:00
View on GitHub
Comments
1
Participants
2
Timeline
13
Reactions
0
Timeline (top)
cross-referenced ×5referenced ×5closed ×1commented ×1

The Telegram retry runner (createTelegramRetryRunner) is well-built but shouldRetry never fires for the most common transient failure class because the regex doesn't match grammY's HttpError message format.

Error Message

[telegram] sendMessage failed: Network request for 'sendMessage' failed! [telegram] block reply failed: HttpError: Network request for 'sendMessage' failed! [telegram] message processing failed: HttpError: Network request for 'sendMessage' failed!

Root Cause

The Telegram retry runner (createTelegramRetryRunner) is well-built but shouldRetry never fires for the most common transient failure class because the regex doesn't match grammY's HttpError message format.

Fix Action

Fixed

PR fix notes

PR #51589: fix(telegram): match grammY HttpError in retry regex

Description (problem / solution / changelog)

Summary

  • Add network request to TELEGRAM_RETRY_RE so grammY HttpError messages trigger the retry path
  • Add test case covering the grammY HttpError message format

Root Cause

grammY wraps underlying network errors in HttpError with the message:

Network request for 'sendMessage' failed!

This string contains none of the existing TELEGRAM_RETRY_RE keywords (429|timeout|connect|reset|closed|unavailable|temporarily), so shouldRetry returns false and retries never fire — even though the underlying cause is a transient network failure (ECONNRESET, UND_ERR_CONNECT_TIMEOUT, etc.).

Fix

Add network request to the regex. This matches grammY's HttpError message format at the top level without needing to traverse .cause, keeping the change minimal and backwards-compatible.

The strictShouldRetry guard for non-idempotent operations (e.g. sendMessage) is unaffected — the regex fallback is only OR'd in for idempotent paths like sendChatAction, setReaction, and deleteMessage.

Test Plan

  • Added test: retries grammY HttpError wrapping network failures via regex
  • All 7 existing + new tests pass
  • Verified strictShouldRetry: true still suppresses the regex fallback

Closes #51525

Changed files

  • src/infra/retry-policy.test.ts (modified, +17/-0)
  • src/infra/retry-policy.ts (modified, +2/-1)

PR #2: fix(telegram): retry network errors wrapped in grammY HttpError

Description (problem / solution / changelog)

Summary

  • Problem: grammY wraps network failures (ECONNRESET, ETIMEDOUT) in HttpError with message Network request for 'sendMessage' failed! and the original error in .cause. formatErrorMessage only checked err.message, so shouldRetry never fired — ~40 silent failures/day on high-latency paths.
  • What changed: formatErrorMessage traverses .cause chain (with cycle protection). Added Network request to TELEGRAM_RETRY_RE.
  • What did NOT change: Retry timing, attempt counts, Discord retry logic, public API surface.

Change Type

  • Bug fix

Scope

  • Integrations

Linked Issue/PR

  • Closes #51525

User-visible / Behavior Changes

Telegram message sends now retry on transient network errors that grammY wraps in HttpError. Previously silently dropped.

Security Impact

  • 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

Repro + Verification

Steps

  1. Throw grammY HttpError wrapping ECONNRESET: throw Object.assign(new Error("Network request for 'sendMessage' failed!"), { cause: new Error("ECONNRESET") })
  2. Observe retry fires

Expected

shouldRetry returns true, retry executes.

Actual (before fix)

shouldRetry returns false, no retry.

Evidence

  • Failing test before + passing after
  • Log snippets from #51525

Human Verification

  • Verified: formatErrorMessage traverses .cause chain, TELEGRAM_RETRY_RE matches grammY HttpError
  • Edge cases: circular .cause (cycle protection), 3+ nesting levels
  • Not verified: live Telegram integration (needs production setup)

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Failure Recovery

  • Revert: git revert HEAD on branch
  • Bad symptoms: retry storms (unlikely — only affects previously-failing paths)

Changed files

  • src/infra/errors.test.ts (modified, +20/-0)
  • src/infra/errors.ts (modified, +15/-0)
  • src/infra/retry-policy.test.ts (modified, +30/-0)
  • src/infra/retry-policy.ts (modified, +1/-1)

PR #51895: fix(telegram): retry network errors wrapped in grammY HttpError

Description (problem / solution / changelog)

Summary

  • Problem: grammY wraps network failures (ECONNRESET, ETIMEDOUT) in HttpError with message Network request for 'sendMessage' failed! and the original error in .cause. formatErrorMessage only checked err.message, so shouldRetry never fired — ~40 silent failures/day on high-latency paths.
  • What changed: formatErrorMessage traverses .cause chain (with cycle protection). Added Network request to TELEGRAM_RETRY_RE.
  • What did NOT change: Retry timing, attempt counts, Discord retry logic, public API surface.

Change Type

  • Bug fix

Scope

  • Integrations

Linked Issue/PR

  • Closes #51525

User-visible / Behavior Changes

Telegram message sends now retry on transient network errors that grammY wraps in HttpError. Previously silently dropped.

Security Impact

  • 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

Repro + Verification

Steps

  1. Throw grammY HttpError wrapping ECONNRESET: throw Object.assign(new Error("Network request for 'sendMessage' failed!"), { cause: new Error("ECONNRESET") })
  2. Observe retry fires

Expected

shouldRetry returns true, retry executes.

Actual (before fix)

shouldRetry returns false, no retry.

Evidence

  • Failing test before + passing after
  • Log snippets from #51525

Human Verification

  • Verified: formatErrorMessage traverses .cause chain, TELEGRAM_RETRY_RE matches grammY HttpError
  • Edge cases: circular .cause (cycle protection), 3+ nesting levels
  • Not verified: live Telegram integration (needs production setup)

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Failure Recovery

  • Revert: git revert HEAD on branch
  • Bad symptoms: retry storms (unlikely — only affects previously-failing paths)

Changed files

  • src/infra/errors.test.ts (modified, +20/-0)
  • src/infra/errors.ts (modified, +17/-0)
  • src/infra/retry-policy.test.ts (modified, +30/-0)
  • src/infra/retry-policy.ts (modified, +2/-1)

PR #3: fix(telegram): retry network errors wrapped in grammY HttpError

Description (problem / solution / changelog)

Summary

Fixes openclaw/openclaw#51525

Telegram delivery retries were failing because grammY wraps network errors (ECONNRESET, etc.) in a HttpError with the actual error in .cause. The retry regex didn't match the outer message.

Changes

  • Added Network request to TELEGRAM_RETRY_RE regex to match grammY's wrapper message
  • Added .cause chain traversal in formatErrorMessage with circular reference protection
  • Added 4 tests: cause chain traversal, circular cause handling, grammY retry via cause, grammY retry via regex

Test plan

  • Unit tests pass
  • Telegram messages that previously failed with ECONNRESET now retry correctly

Changed files

  • src/infra/errors.test.ts (modified, +20/-0)
  • src/infra/errors.ts (modified, +17/-0)
  • src/infra/retry-policy.test.ts (modified, +30/-0)
  • src/infra/retry-policy.ts (modified, +2/-1)

PR #52000: Telegram: retry when transient errno is nested under HttpError.cause (#51525)

Description (problem / solution / changelog)

Summary

Include Error.cause messages and code fields when evaluating the default Telegram retry regex, so grammY Network request for … failed! errors still retry when the root cause is ECONNRESET / similar.

Test plan

  • pnpm test -- src/infra/retry-policy.test.ts

Fixes #51525

Made with Cursor

Changed files

  • src/infra/retry-policy.test.ts (modified, +19/-0)
  • src/infra/retry-policy.ts (modified, +20/-3)

Code Example

const TELEGRAM_RETRY_RE = /429|timeout|connect|reset|closed|unavailable|temporarily/i;

---

Network request for 'sendMessage' failed!

---

[telegram] sendMessage failed: Network request for 'sendMessage' failed!
[telegram] block reply failed: HttpError: Network request for 'sendMessage' failed!
[telegram] message processing failed: HttpError: Network request for 'sendMessage' failed!
RAW_BUFFERClick to expand / collapse

Summary

The Telegram retry runner (createTelegramRetryRunner) is well-built but shouldRetry never fires for the most common transient failure class because the regex doesn't match grammY's HttpError message format.

Environment

  • OpenClaw 2026.3.13
  • Node 22, grammY, undici
  • High-latency path (Malaysia → Amsterdam, ~530ms RTT)

Details

resolveTelegramShouldRetry checks error messages against:

const TELEGRAM_RETRY_RE = /429|timeout|connect|reset|closed|unavailable|temporarily/i;

grammY wraps underlying network errors in HttpError with the message:

Network request for 'sendMessage' failed!

This string contains none of the regex keywords. The actual error codes (ECONNRESET, UND_ERR_CONNECT_TIMEOUT, ETIMEDOUT) are in .cause, but formatErrorMessage only checks the top-level message.

Result: shouldRetry returns false. Retries never fire for network errors despite TELEGRAM_RETRY_DEFAULTS being correctly configured (3 attempts, 400ms base).

Observed log pattern (~40 failures/day)

[telegram] sendMessage failed: Network request for 'sendMessage' failed!
[telegram] block reply failed: HttpError: Network request for 'sendMessage' failed!
[telegram] message processing failed: HttpError: Network request for 'sendMessage' failed!

Suggested fix

Either:

  1. formatErrorMessage should traverse .cause to include nested error codes/messages (cleaner, benefits all retry paths), OR
  2. Add "Network request" to TELEGRAM_RETRY_RE (targeted fix)

Related: #17521 (locked, covers missing retry logic — this issue identifies why the existing retry logic doesn't fire)

extent analysis

Fix Plan

To fix the issue, we will modify the formatErrorMessage function to traverse the .cause property and include nested error codes/messages in the error message. This approach is cleaner and benefits all retry paths.

Step-by-Step Solution

  • Modify the formatErrorMessage function to recursively check the .cause property:
function formatErrorMessage(error) {
  let message = error.message;
  if (error.cause) {
    message += `: ${formatErrorMessage(error.cause)}`;
  }
  return message;
}
  • Update the resolveTelegramShouldRetry function to use the modified formatErrorMessage function:
function resolveTelegramShouldRetry(error) {
  const errorMessage = formatErrorMessage(error);
  return TELEGRAM_RETRY_RE.test(errorMessage);
}
  • Alternatively, you can add "Network request" to TELEGRAM_RETRY_RE as a targeted fix:
const TELEGRAM_RETRY_RE = /429|timeout|connect|reset|closed|unavailable|temporarily|Network request/i;

However, the first approach is recommended as it provides a more comprehensive solution.

Verification

To verify that the fix worked, you can test the resolveTelegramShouldRetry function with a sample error object that has a nested error cause:

const error = new Error('Network request for \'sendMessage\' failed!');
error.cause = new Error('ECONNRESET');
console.log(resolveTelegramShouldRetry(error)); // Should print: true

Extra Tips

  • Make sure to test the fix thoroughly to ensure that it works as expected in different scenarios.
  • Consider adding more error codes to the TELEGRAM_RETRY_RE regex to handle other potential transient failures.
  • Keep in mind that this fix assumes that the .cause property is always present when there is a nested error. If this is not the case, additional error handling may be necessary.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING