openclaw - ✅(Solved) Fix Telegram retry regex too strict: bare grammy `Network request for 'X' failed!` (no "after") never classified as recoverable for `context: send`, drops outbound messages [1 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#80362Fetched 2026-05-11 03:15:31
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
2
Timeline (top)
cross-referenced ×2commented ×1

isRecoverableTelegramNetworkError(err, { context: "send" }) in extensions/telegram/src/network-errors.ts (compiled dist/send-*.js) fails to classify grammy's bare pre-connect form Network request for 'sendChatAction' failed! as recoverable — so the per-request shouldRetry predicate in createTelegramRequestWithDiag returns false and the user-visible reply is silently dropped.

Error Message

| Pre-connect | Network request for 'sendChatAction' failed! | fetch aborted before bytes hit the wire (event-loop starvation timer, dead-pool socket synchronous error, DNS failure). Safe to retry — message did NOT reach Telegram. | completely unrelated error message

Root Cause

The regex used to whitelist grammy network errors only matches the post-connect form:

// dist/send-BZcD66uc.js (compiled from extensions/telegram/src/network-errors.ts)
const GRAMMY_NETWORK_REQUEST_FAILED_AFTER_RE =
    /^network request(?:\s+for\s+["']?[^"']+["']?)?\s+failed\s+after\b.*[!.]?$/i;

…but grammy raises two distinct forms:

FormExample messageMeaning
Pre-connectNetwork request for 'sendChatAction' failed!fetch aborted before bytes hit the wire (event-loop starvation timer, dead-pool socket synchronous error, DNS failure). Safe to retry — message did NOT reach Telegram.
Post-connectNetwork request for 'sendChatAction' failed after 5 attempts: ...grammy's own retry budget exhausted. Risky to retry — message MAY have been delivered.

The current regex requires the literal failed\s+after\b, so the pre-connect form (the safer one to retry) is rejected, while the post-connect form (the riskier one) passes.

For context: "send", allowMessageMatch=false is set deliberately to avoid retrying the post-connect form and producing duplicate user messages. That's correct for the post-connect case — but it also skips the snippet check RECOVERABLE_MESSAGE_SNIPPETS (which DOES contain "network request"), leaving only the broken regex as the gatekeeper.

Net effect: a single transient socket-pool failure during event-loop starvation drops the user's reply with no retry.

Fix Action

Workaround

Patch dist/send-*.js post-install (we wired this into a post-openclaw-upgrade.sh hook to survive package updates).

PR fix notes

PR #80367: fix(telegram): classify bare pre-connect grammY errors as recoverable for send context

Description (problem / solution / changelog)

Root cause

grammY raises two distinct network error forms:

FormExampleSafe to retry?
Pre-connectNetwork request for 'sendMessage' failed!Yes — socket dropped before bytes hit the wire; message never delivered
Post-connectNetwork request for 'sendMessage' failed after 2 attempts.Risky — grammy's own retry budget exhausted; message may have reached Telegram

isRecoverableTelegramNetworkError with context: "send" sets allowMessageMatch=false, which blocks the RECOVERABLE_MESSAGE_SNIPPETS path. The existing GRAMMY_NETWORK_REQUEST_FAILED_AFTER_RE only matches the post-connect form. The pre-connect form falls through unclassified → shouldRetry returns false → reply silently dropped.

Fix

Add GRAMMY_NETWORK_REQUEST_FAILED_BARE_RE for the pre-connect form (/^network request(?:...)?\\s+failed[!.]?\\s*$/i) and check it unconditionally (before the allowMessageMatch gate), alongside the existing post-connect check. Non-grammy snippet matches (e.g. "undici") remain blocked for send context.

Files changed

  • extensions/telegram/src/network-errors.ts — add GRAMMY_NETWORK_REQUEST_FAILED_BARE_RE, apply it in isRecoverableTelegramNetworkError
  • extensions/telegram/src/network-errors.test.ts — update test to assert pre-connect form is now true for send context; split into two focused tests

Real behavior proof

Behavior or issue addressed: isRecoverableTelegramNetworkError(new Error("Network request for 'sendMessage' failed!"), { context: "send" }) returned false (reply dropped). Now returns true (retry triggered).

Real environment tested: DGX workstation, Node.js v22, pnpm vitest runner.

Exact steps or command run after this patch:

pnpm exec vitest run extensions/telegram/src/network-errors.test.ts --reporter=verbose

Evidence after fix:

 ✓ isRecoverableTelegramNetworkError > classifies bare pre-connect grammY errors as recoverable for send context 0ms
 ✓ isRecoverableTelegramNetworkError > skips broad message-snippet matches for send context 0ms
 ✓ isRecoverableTelegramNetworkError > treats grammY failed-after envelope errors as recoverable in send context 0ms

 Test Files  1 passed (1)
      Tests  43 passed (43)
   Start at  01:47:30
   Duration  238ms

Before: isRecoverableTelegramNetworkError(bareErr, { context: "send" })false After: → true (pre-connect form matched by new bare regex, bypassing allowMessageMatch gate)

Observed result after fix: 43/43 tests pass. The bare pre-connect form is now classified recoverable for send context, matching the intent documented in the issue. Non-grammy broad snippets ("undici", "network request") remain blocked for send context.

What was not tested: Live Telegram send path under event-loop starvation — the fix is a regex addition in network-errors.ts; the behavioral change is exercised via the network-errors unit tests.

Fixes #80362

Changed files

  • extensions/ollama/src/stream-runtime.test.ts (modified, +18/-12)
  • extensions/ollama/src/stream.ts (modified, +8/-5)
  • extensions/telegram/src/network-errors.test.ts (modified, +7/-2)
  • extensions/telegram/src/network-errors.ts (modified, +8/-0)
  • src/commands/sessions-table.ts (modified, +20/-0)

Code Example

// dist/send-BZcD66uc.js (compiled from extensions/telegram/src/network-errors.ts)
const GRAMMY_NETWORK_REQUEST_FAILED_AFTER_RE =
    /^network request(?:\s+for\s+["']?[^"']+["']?)?\s+failed\s+after\b.*[!.]?$/i;

---

- const GRAMMY_NETWORK_REQUEST_FAILED_AFTER_RE =
-     /^network request(?:\s+for\s+["']?[^"']+["']?)?\s+failed\s+after\b.*[!.]?$/i;
+ const GRAMMY_NETWORK_REQUEST_FAILED_RE =
+     /^network request(?:\s+for\s+["']?[^"']+["']?)?\s+failed(?:\s+after\b.*)?[!.]?$/i;

---

Network request for 'sendChatAction' failed!              ← pre-connect, currently DROPS
Network request for 'sendChatAction' failed after 5 attempts.   post-connect, currently matches
Network request failed                                    ← bare, currently DROPS
Network request for getMe failed.                          currently DROPS

---

telegram returned 401 unauthorized
ETIMEDOUT
completely unrelated error message
RAW_BUFFERClick to expand / collapse

Summary

isRecoverableTelegramNetworkError(err, { context: "send" }) in extensions/telegram/src/network-errors.ts (compiled dist/send-*.js) fails to classify grammy's bare pre-connect form Network request for 'sendChatAction' failed! as recoverable — so the per-request shouldRetry predicate in createTelegramRequestWithDiag returns false and the user-visible reply is silently dropped.

Root cause

The regex used to whitelist grammy network errors only matches the post-connect form:

// dist/send-BZcD66uc.js (compiled from extensions/telegram/src/network-errors.ts)
const GRAMMY_NETWORK_REQUEST_FAILED_AFTER_RE =
    /^network request(?:\s+for\s+["']?[^"']+["']?)?\s+failed\s+after\b.*[!.]?$/i;

…but grammy raises two distinct forms:

FormExample messageMeaning
Pre-connectNetwork request for 'sendChatAction' failed!fetch aborted before bytes hit the wire (event-loop starvation timer, dead-pool socket synchronous error, DNS failure). Safe to retry — message did NOT reach Telegram.
Post-connectNetwork request for 'sendChatAction' failed after 5 attempts: ...grammy's own retry budget exhausted. Risky to retry — message MAY have been delivered.

The current regex requires the literal failed\s+after\b, so the pre-connect form (the safer one to retry) is rejected, while the post-connect form (the riskier one) passes.

For context: "send", allowMessageMatch=false is set deliberately to avoid retrying the post-connect form and producing duplicate user messages. That's correct for the post-connect case — but it also skips the snippet check RECOVERABLE_MESSAGE_SNIPPETS (which DOES contain "network request"), leaving only the broken regex as the gatekeeper.

Net effect: a single transient socket-pool failure during event-loop starvation drops the user's reply with no retry.

Reproduction

  1. Run openclaw gateway under CPU pressure on a low-resource machine (we hit it on a 2GB Asus E200HA with concurrent model calls).
  2. Observe periodic [diagnostic] liveness warning: ... eventLoopDelayMaxMs=15000 and accompanying [fetch-timeout] timer delayed 33208ms, likely event-loop starvation.
  3. Around the starvation window, watch grammy raise Network request for 'sendChatAction' failed! (no "after").
  4. Confirm [telegram] message processing failed: HttpError: Network request for 'sendMessage' failed! — single attempt, no retry.

Repro on our box: 2026-05-10 20:15:19-20:17:47 — 39 send failures over 140s, all single-attempt, no retry log lines. v2026.5.3-1.

Suggested fix

Make the post-connect after ... clause optional — the regex still matches both forms safely:

- const GRAMMY_NETWORK_REQUEST_FAILED_AFTER_RE =
-     /^network request(?:\s+for\s+["']?[^"']+["']?)?\s+failed\s+after\b.*[!.]?$/i;
+ const GRAMMY_NETWORK_REQUEST_FAILED_RE =
+     /^network request(?:\s+for\s+["']?[^"']+["']?)?\s+failed(?:\s+after\b.*)?[!.]?$/i;

Test cases (all should match):

Network request for 'sendChatAction' failed!              ← pre-connect, currently DROPS
Network request for 'sendChatAction' failed after 5 attempts.  ← post-connect, currently matches
Network request failed                                    ← bare, currently DROPS
Network request for getMe failed.                         ← currently DROPS

Negative cases (should NOT match):

telegram returned 401 unauthorized
ETIMEDOUT
completely unrelated error message

Verified locally — old regex matches 1/4 positive cases; new regex matches 4/4 with no false positives.

Why this is safe

The pre-connect form happens specifically when grammy's internal fetch aborts before any bytes are written — either via AbortSignal (timeout firing) or synchronously from a dead undici pool socket. In both cases the request never reached api.telegram.org, so retry cannot create a duplicate.

Related

  • #48029 — Telegram long-polling stalls when undici keep-alive pool not recycled (same underlying cause: dead pool sockets after NAT idle eviction)
  • #41704 — Telegram polling stalls when proxy TCP connection drops silently
  • #72338 — Gateway CPU spin causes Telegram replies to stall

Workaround

Patch dist/send-*.js post-install (we wired this into a post-openclaw-upgrade.sh hook to survive package updates).

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

openclaw - ✅(Solved) Fix Telegram retry regex too strict: bare grammy `Network request for 'X' failed!` (no "after") never classified as recoverable for `context: send`, drops outbound messages [1 pull requests, 1 comments, 2 participants]