Skip to content

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

Merged
obviyus merged 8 commits into
openclaw:mainfrom
chinar-amrutkar:fix/51525-telegram-retry-regex
Apr 1, 2026
Merged

fix(telegram): retry network errors wrapped in grammY HttpError#51895
obviyus merged 8 commits into
openclaw:mainfrom
chinar-amrutkar:fix/51525-telegram-retry-regex

Conversation

@chinar-amrutkar

Copy link
Copy Markdown
Contributor

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

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

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)

@greptile-apps

greptile-apps Bot commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a real, well-described production bug: grammY wraps transient network failures (ECONNRESET, ETIMEDOUT) inside an HttpError with the original error in .cause, so formatErrorMessage — which only read err.message — never produced a string that matched TELEGRAM_RETRY_RE, silently dropping retries. The two-part fix is correct and minimal: traverse .cause chains in formatErrorMessage, and add Network request to the retry regex.

Key observations:

  • The .cause traversal is well-guarded against infinite loops via a Set; however err itself is not seeded into the set, so a cycle that includes the root error (e.g. a.cause = b; b.cause = a when starting from a) appends err.message a second time before the guard fires. The existing cycle test uses .toContain and misses this. See inline comment on errors.ts:73-74.
  • Network request is intentionally generic enough to catch any grammY HttpError on a network call, which is the right scope for Telegram retry.
  • Discord retry logic, retry timing, and attempt counts are untouched as stated.
  • Tests cover both the linear cause chain (grammY's actual shape) and the circular-reference guard.

Confidence Score: 4/5

  • Safe to merge — the fix is correct for all real-world inputs; the only issue is a cosmetic duplicate in circular-error formatting that never arises in production.
  • The core logic (cause traversal + regex) is correct and well-tested. The one real finding — err not being seeded into seen causing a duplicated root message on a.cause=b; b.cause=a cycles — is cosmetic (an extra " | error A" in an already-abnormal error string) and has zero impact on the retry behaviour this PR is fixing. All other concerns (non-idempotency, Discord side effects) are explicitly handled by the pre-existing strictShouldRetry mechanism and are unchanged.
  • src/infra/errors.ts — cycle-guard seed, and the corresponding test in src/infra/errors.test.ts
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/infra/errors.ts
Line: 73-74

Comment:
**Cycle detection misses the root error itself**

`err` is never added to `seen`, so a cycle that includes `err` is only broken after `err.message` has been appended a second time. For the test case `a.cause = b; b.cause = a`, the loop produces `"error A | error B | error A"``err.message` appears twice. The existing test uses `.toContain` and therefore doesn't catch the duplicate.

Initialize `seen` with `err` to close that gap:

```suggestion
    let cause: unknown = err.cause;
    const seen = new Set<unknown>([err]);
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "fix(telegram): trave..."

Comment thread src/infra/errors.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 882980d7f1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/errors.ts
Comment thread src/infra/retry-policy.ts Outdated
@chinar-amrutkar chinar-amrutkar force-pushed the fix/51525-telegram-retry-regex branch from 7c99269 to ae44696 Compare April 1, 2026 09:37
@chinar-amrutkar

Copy link
Copy Markdown
Contributor Author

@obviyus could you kindly review this when you have a moment?

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ae4469687a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/retry-policy.ts Outdated
Comment thread src/infra/errors.ts Outdated
@openclaw-barnacle openclaw-barnacle Bot added the channel: telegram Channel integration: telegram label Apr 1, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 68e43b0878

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread extensions/telegram/src/send.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e0972e67af

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread extensions/telegram/src/send.ts
@obviyus obviyus self-assigned this Apr 1, 2026
chinar-amrutkar and others added 7 commits April 1, 2026 15:42
…match grammY HttpError

grammY wraps network failures in HttpError with message
'Network request for ... failed!' and the original error in .cause.
formatErrorMessage only checked err.message, so shouldRetry never
fired for the most common transient failure class.

Changes:
- formatErrorMessage now traverses .cause chain, appending nested
  error messages (with cycle protection)
- Added 'Network request' to TELEGRAM_RETRY_RE as belt-and-suspenders
- Added tests for .cause traversal, circular references, and grammY
  HttpError retry behavior

Fixes openclaw#51525
@obviyus obviyus force-pushed the fix/51525-telegram-retry-regex branch from 6694b44 to 068a177 Compare April 1, 2026 10:14

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 068a177fa2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread extensions/telegram/src/network-errors.ts Outdated

@obviyus obviyus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed latest changes; landing now.

@obviyus obviyus merged commit 3f67581 into openclaw:main Apr 1, 2026
9 checks passed
@obviyus

obviyus commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Landed on main.

Thanks @chinar-amrutkar.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cd03366d4d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

export function isTelegramRateLimitError(err: unknown): boolean {
return (
hasTelegramErrorCode(err, (code) => code === 429) ||
(hasTelegramRetryAfter(err) && /(?:^|\b)429\b|too many requests/i.test(formatErrorMessage(err)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Propagate wrapped retry_after delay into strict send retries

isTelegramRateLimitError now treats nested wrappers as retriable (via hasTelegramRetryAfter), but the strict send path in createTelegramNonIdempotentRequestWithDiag still relies on createTelegramRetryRunner's shallow getTelegramRetryAfterMs parser, which only reads top-level parameters/response/error.parameters. For wrapped shapes like HttpError -> error.response.parameters.retry_after, this change will retry immediately using default backoff instead of Telegram’s required delay, so attempts can be exhausted under throttling even though a valid retry_after was present.

Useful? React with 👍 / 👎.

@chinar-amrutkar chinar-amrutkar deleted the fix/51525-telegram-retry-regex branch April 1, 2026 21:40
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
@chinar-amrutkar)

* fix(telegram): traverse error .cause chain in formatErrorMessage and match grammY HttpError

grammY wraps network failures in HttpError with message
'Network request for ... failed!' and the original error in .cause.
formatErrorMessage only checked err.message, so shouldRetry never
fired for the most common transient failure class.

Changes:
- formatErrorMessage now traverses .cause chain, appending nested
  error messages (with cycle protection)
- Added 'Network request' to TELEGRAM_RETRY_RE as belt-and-suspenders
- Added tests for .cause traversal, circular references, and grammY
  HttpError retry behavior

Fixes openclaw#51525

* style: fix oxfmt formatting in retry-policy.ts

* fix: add braces to satisfy oxlint requirement

* fix(telegram): keep send retries strict

* test(telegram): cover wrapped retry paths

* fix(telegram): retry rate-limited sends safely

* fix: retry safe wrapped Telegram send failures (openclaw#51895) (thanks @chinar-amrutkar)

* fix: preserve wrapped Telegram rate-limit retries (openclaw#51895) (thanks @chinar-amrutkar)

---------

Co-authored-by: chinar-amrutkar <chinar-amrutkar@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
@chinar-amrutkar)

* fix(telegram): traverse error .cause chain in formatErrorMessage and match grammY HttpError

grammY wraps network failures in HttpError with message
'Network request for ... failed!' and the original error in .cause.
formatErrorMessage only checked err.message, so shouldRetry never
fired for the most common transient failure class.

Changes:
- formatErrorMessage now traverses .cause chain, appending nested
  error messages (with cycle protection)
- Added 'Network request' to TELEGRAM_RETRY_RE as belt-and-suspenders
- Added tests for .cause traversal, circular references, and grammY
  HttpError retry behavior

Fixes openclaw#51525

* style: fix oxfmt formatting in retry-policy.ts

* fix: add braces to satisfy oxlint requirement

* fix(telegram): keep send retries strict

* test(telegram): cover wrapped retry paths

* fix(telegram): retry rate-limited sends safely

* fix: retry safe wrapped Telegram send failures (openclaw#51895) (thanks @chinar-amrutkar)

* fix: preserve wrapped Telegram rate-limit retries (openclaw#51895) (thanks @chinar-amrutkar)

---------

Co-authored-by: chinar-amrutkar <chinar-amrutkar@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
@chinar-amrutkar)

* fix(telegram): traverse error .cause chain in formatErrorMessage and match grammY HttpError

grammY wraps network failures in HttpError with message
'Network request for ... failed!' and the original error in .cause.
formatErrorMessage only checked err.message, so shouldRetry never
fired for the most common transient failure class.

Changes:
- formatErrorMessage now traverses .cause chain, appending nested
  error messages (with cycle protection)
- Added 'Network request' to TELEGRAM_RETRY_RE as belt-and-suspenders
- Added tests for .cause traversal, circular references, and grammY
  HttpError retry behavior

Fixes openclaw#51525

* style: fix oxfmt formatting in retry-policy.ts

* fix: add braces to satisfy oxlint requirement

* fix(telegram): keep send retries strict

* test(telegram): cover wrapped retry paths

* fix(telegram): retry rate-limited sends safely

* fix: retry safe wrapped Telegram send failures (openclaw#51895) (thanks @chinar-amrutkar)

* fix: preserve wrapped Telegram rate-limit retries (openclaw#51895) (thanks @chinar-amrutkar)

---------

Co-authored-by: chinar-amrutkar <chinar-amrutkar@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
@chinar-amrutkar)

* fix(telegram): traverse error .cause chain in formatErrorMessage and match grammY HttpError

grammY wraps network failures in HttpError with message
'Network request for ... failed!' and the original error in .cause.
formatErrorMessage only checked err.message, so shouldRetry never
fired for the most common transient failure class.

Changes:
- formatErrorMessage now traverses .cause chain, appending nested
  error messages (with cycle protection)
- Added 'Network request' to TELEGRAM_RETRY_RE as belt-and-suspenders
- Added tests for .cause traversal, circular references, and grammY
  HttpError retry behavior

Fixes openclaw#51525

* style: fix oxfmt formatting in retry-policy.ts

* fix: add braces to satisfy oxlint requirement

* fix(telegram): keep send retries strict

* test(telegram): cover wrapped retry paths

* fix(telegram): retry rate-limited sends safely

* fix: retry safe wrapped Telegram send failures (openclaw#51895) (thanks @chinar-amrutkar)

* fix: preserve wrapped Telegram rate-limit retries (openclaw#51895) (thanks @chinar-amrutkar)

---------

Co-authored-by: chinar-amrutkar <chinar-amrutkar@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Telegram retry regex misses grammY HttpError format — retries never fire for network errors

2 participants