Skip to content

fix(telegram): backoff for transient network errors in sendChatAction#55838

Closed
sumaiazaman wants to merge 2 commits into
openclaw:mainfrom
sumaiazaman:fix/telegram-sendchataction-network-backoff
Closed

fix(telegram): backoff for transient network errors in sendChatAction#55838
sumaiazaman wants to merge 2 commits into
openclaw:mainfrom
sumaiazaman:fix/telegram-sendchataction-network-backoff

Conversation

@sumaiazaman

Copy link
Copy Markdown

Summary

  • Adds exponential backoff for transient network errors (connection reset, timeout, fetch failures) in the sendChatAction handler
  • Swallows transient errors instead of re-throwing (sendChatAction is best-effort)
  • Logs only on first transient failure to prevent log spam
  • Suspends after 5 consecutive transient failures (configurable via maxConsecutiveTransient)
  • Resets counters on success

Closes #55811

Test plan

  • 10 tests pass including 4 new transient error tests
  • pnpm check passes

@openclaw-barnacle openclaw-barnacle Bot added channel: telegram Channel integration: telegram size: S labels Mar 27, 2026
@greptile-apps

greptile-apps Bot commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extends the existing sendChatAction 401-backoff circuit-breaker to also handle transient network errors (ECONNRESET, ETIMEDOUT, fetch failures, etc.), treating sendChatAction as best-effort by swallowing those errors rather than propagating them, and suspending after a configurable number of consecutive network failures.

  • The dual-counter design (consecutive401Failures / consecutiveTransientFailures) with mutual reset on success and on the opposing error type is correct; only one counter can be non-zero at a time, so totalFailures correctly drives the shared backoff.
  • isTransientNetworkError uses a broad lower.includes("network request") fragment that could accidentally classify an API-level error (e.g. "network request returned status 500") as transient and silently swallow it — narrowing to lower.includes("network request for") would match the actual SDK pattern without the wider risk.
  • The backoff log message was simplified to (failure N), dropping the N/max indicator that was useful for operators monitoring 401-rate logs.
  • Test coverage for the new paths is thorough (4 new tests, existing test updated).

Confidence Score: 5/5

Safe to merge — logic is correct and well-tested; remaining findings are P2 style improvements.

All findings are P2 (broad substring match, missing max in log message). No correctness, data-integrity, or reliability issues found. The dual-counter backoff logic is sound and all new behaviour is covered by tests.

sendchataction-401-backoff.ts — minor: tighten the 'network request' substring predicate and restore the N/max indicator in the backoff log.

Important Files Changed

Filename Overview
extensions/telegram/src/sendchataction-401-backoff.ts Adds transient network error detection and a second consecutive-failure counter integrated into the shared backoff path. Two minor style concerns: broad 'network request' substring and missing N/max in backoff log.
extensions/telegram/src/sendchataction-401-backoff.test.ts Adds four new test cases covering transient error swallowing, backoff triggering, suspension, and counter reset on success. Existing tests updated correctly.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/telegram/src/sendchataction-401-backoff.ts
Line: 72

Comment:
**Overly broad "network request" substring match**

`lower.includes("network request")` is a very generic fragment. An API error like `"network request returned status 500"` or `"rate-limited: too many network requests"` would match this predicate and be silently swallowed as a transient error. The other patterns (`econnreset`, `etimedout`, `fetch failed`, `socket hang up`) are specific low-level I/O error codes, but this one could absorb HTTP-level failures from Telegram's API itself.

Consider tightening the match to the actual Node.js fetch/undici error message format:
```suggestion
    lower.includes("network request for") ||
```
This still catches the documented Telegram bot SDK pattern (`"Network request for 'sendChatAction' failed!"`) without matching unrelated strings containing "network request".

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

---

This is a comment left during a code review.
Path: extensions/telegram/src/sendchataction-401-backoff.ts
Line: 119-121

Comment:
**Backoff log no longer shows suspension threshold**

The previous message included `(failure ${consecutive401Failures}/${maxConsecutive401})`, giving operators an at-a-glance view of how many more failures remain before suspension. The new unified message `(failure ${totalFailures})` drops the max, which makes it harder to triage a stream of 401 backoff logs in production.

Consider restoring context-sensitive max information:
```suggestion
      logger(
        `sendChatAction backoff: waiting ${backoffMs}ms before retry ` +
          `(failure ${totalFailures}/${consecutive401Failures > 0 ? maxConsecutive401 : maxConsecutiveTransient})`,
      );
```

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

Reviews (1): Last reviewed commit: "fix(telegram): add backoff for transient..." | Re-trigger Greptile

const message = error instanceof Error ? error.message : JSON.stringify(error);
const lower = message.toLowerCase();
return (
lower.includes("network request") ||

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.

P2 Overly broad "network request" substring match

lower.includes("network request") is a very generic fragment. An API error like "network request returned status 500" or "rate-limited: too many network requests" would match this predicate and be silently swallowed as a transient error. The other patterns (econnreset, etimedout, fetch failed, socket hang up) are specific low-level I/O error codes, but this one could absorb HTTP-level failures from Telegram's API itself.

Consider tightening the match to the actual Node.js fetch/undici error message format:

Suggested change
lower.includes("network request") ||
lower.includes("network request for") ||

This still catches the documented Telegram bot SDK pattern ("Network request for 'sendChatAction' failed!") without matching unrelated strings containing "network request".

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/telegram/src/sendchataction-401-backoff.ts
Line: 72

Comment:
**Overly broad "network request" substring match**

`lower.includes("network request")` is a very generic fragment. An API error like `"network request returned status 500"` or `"rate-limited: too many network requests"` would match this predicate and be silently swallowed as a transient error. The other patterns (`econnreset`, `etimedout`, `fetch failed`, `socket hang up`) are specific low-level I/O error codes, but this one could absorb HTTP-level failures from Telegram's API itself.

Consider tightening the match to the actual Node.js fetch/undici error message format:
```suggestion
    lower.includes("network request for") ||
```
This still catches the documented Telegram bot SDK pattern (`"Network request for 'sendChatAction' failed!"`) without matching unrelated strings containing "network request".

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

Comment on lines 119 to 121
`sendChatAction backoff: waiting ${backoffMs}ms before retry ` +
`(failure ${consecutive401Failures}/${maxConsecutive401})`,
`(failure ${totalFailures})`,
);

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.

P2 Backoff log no longer shows suspension threshold

The previous message included (failure ${consecutive401Failures}/${maxConsecutive401}), giving operators an at-a-glance view of how many more failures remain before suspension. The new unified message (failure ${totalFailures}) drops the max, which makes it harder to triage a stream of 401 backoff logs in production.

Consider restoring context-sensitive max information:

Suggested change
`sendChatAction backoff: waiting ${backoffMs}ms before retry ` +
`(failure ${consecutive401Failures}/${maxConsecutive401})`,
`(failure ${totalFailures})`,
);
logger(
`sendChatAction backoff: waiting ${backoffMs}ms before retry ` +
`(failure ${totalFailures}/${consecutive401Failures > 0 ? maxConsecutive401 : maxConsecutiveTransient})`,
);
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/telegram/src/sendchataction-401-backoff.ts
Line: 119-121

Comment:
**Backoff log no longer shows suspension threshold**

The previous message included `(failure ${consecutive401Failures}/${maxConsecutive401})`, giving operators an at-a-glance view of how many more failures remain before suspension. The new unified message `(failure ${totalFailures})` drops the max, which makes it harder to triage a stream of 401 backoff logs in production.

Consider restoring context-sensitive max information:
```suggestion
      logger(
        `sendChatAction backoff: waiting ${backoffMs}ms before retry ` +
          `(failure ${totalFailures}/${consecutive401Failures > 0 ? maxConsecutive401 : maxConsecutiveTransient})`,
      );
```

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

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 89bb289a19

ℹ️ 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 on lines +170 to 174
// Swallow transient errors — sendChatAction is best-effort
return;
}
throw error;
}

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.

P2 Badge Reset transient counter on non-transient errors

The new maxConsecutiveTransient logic only clears consecutiveTransientFailures on success or a 401, but not when another non-transient error is thrown. This means transient failures are treated as "consecutive" across intervening 5xx/other errors, so a sequence like transient → 500 → transient can still hit suspension even though the network failures were not consecutive. That can incorrectly suspend sendChatAction and apply backoff based on stale transient state.

Useful? React with 👍 / 👎.

@sumaiazaman

Copy link
Copy Markdown
Author

Resolved both review findings:

  1. Tightened network error match — narrowed "network request" to "network request for" to avoid accidentally matching HTTP-level errors like "network request returned status 500".
  2. Restored failure threshold in log — backoff message now shows (failure N/max) with context-sensitive max based on whether the current failures are 401 or transient.

See commit f8de59f.

@clawsweeper

clawsweeper Bot commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Closing this as duplicate or superseded after Codex automated review.

Close PR #55838 as superseded. Current main no longer matches the original retry-spam failure mode because Telegram typing goes through the shared typing lifecycle and stops the keepalive loop after consecutive start failures. The narrower handler-level transient cooldown work is now tracked by the newer open PR #55886, which came from the #55811 discussion and covers the same Telegram sendChatAction surface with review follow-ups.

Best possible solution:

Close #55838 as superseded, keep the shipped shared typing breaker behavior on main, and route any remaining Telegram sendChatAction transient cooldown work through the newer canonical PR #55886.

What I checked:

So I’m closing this here and keeping the remaining discussion on the canonical linked item.

Codex Review notes: model gpt-5.5, reasoning high; reviewed against 7e376e5aba32.

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: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Telegram sendChatAction retry spam during transient failures

1 participant