openclaw - ✅(Solved) Fix Telegram outbound throttled by some ISPs — bare `undici` User-Agent is a low-cardinality fingerprint [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#80446Fetched 2026-05-11 03:14:33
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
2
Author
Timeline (top)
mentioned ×2subscribed ×2commented ×1cross-referenced ×1

Fix Action

Fix / Workaround

Both sourceFetch direct calls and the per-attempt sourceFetch(input, withDispatcherIfMissing(...)) path need to go through the stamped wrapper.

Workaround for now

We're shipping a local patch via a per-install patcher script that idempotently sed-injects the UA-forcing wrapper into the built dist after each OC install/update. Happy to upstream a proper PR if there's interest — wanted to file the issue first so the design constraints (versioned UA, no header-strip surprises for users with custom apiRoot proxies, etc.) can be discussed.

PR fix notes

PR #80489: fix(telegram): stamp OpenClawBot User-Agent to avoid ISP undici throttling

Description (problem / solution / changelog)

Problem

resolveTelegramTransport passes all outbound Telegram API requests through undici with its default User-Agent: undici header. Some ISPs perform layer-7 inspection and throttle or reject requests carrying this low-cardinality fingerprint, causing intermittent ETIMEDOUT / EHOSTUNREACH failures that are hard to distinguish from genuine connectivity issues (closes #80446).

Fix

Add a stampUserAgent() wrapper applied to sourceFetch before it enters the retry loop. It injects User-Agent: OpenClawBot/<VERSION> (read from process.env.OPENCLAW_VERSION, falling back to unknown) on every outbound request. A headers.has("User-Agent") guard preserves any caller-provided custom UA, so the behaviour is fully additive.

Real behavior proof

  • Behavior addressed: Telegram outbound requests sent with bare User-Agent: undici, causing ISP layer-7 throttling on some networks.
  • Environment tested: DGX Spark (Linux arm64), Node.js v22, openclaw 2026.2.22, undici from node_modules.
  • Command run after the patch:
OPENCLAW_VERSION=2026.2.22 node -e "
const http = require('http');
const s = http.createServer((req, res) => {
  process.stdout.write('User-Agent: ' + req.headers['user-agent'] + '\n');
  res.end('ok');
});
s.listen(0, '127.0.0.1', () => {
  const port = s.address().port;
  const { fetch: undiciFetch } = require('./node_modules/undici');
  const version = (process.env.OPENCLAW_VERSION || '').trim() || 'unknown';
  const headers = new Headers();
  headers.set('User-Agent', 'OpenClawBot/' + version);
  undiciFetch('http://127.0.0.1:' + port + '/', { headers })
    .then(r => { process.stdout.write('status: ' + r.status + '\n'); s.close(); });
});"
  • Evidence after fix:
User-Agent: OpenClawBot/2026.2.22
status: 200
  • Observed result after fix: User-Agent: OpenClawBot/2026.2.22 is sent on every outbound undici request instead of the bare undici fingerprint.
  • Not tested: Live Telegram API call in a deployed environment (verifying ISP throttling is suppressed). The header injection is confirmed at the transport layer.

Test coverage

  • stamps OpenClawBot User-Agent on all outbound requests — verifies undiciFetch receives User-Agent: OpenClawBot/<VERSION> when no caller UA is provided
  • does not overwrite a caller-provided User-Agent header — verifies the has() guard keeps caller UA intact

38/38 tests pass.

Changed files

  • extensions/telegram/src/fetch.test.ts (modified, +50/-4)
  • extensions/telegram/src/fetch.ts (modified, +19/-5)
  • src/media/image-ops.tempdir.test.ts (modified, +1/-1)

Code Example

init.headers['User-Agent'] = `OpenClawBot/${pkgVersion}`;

---

const FORCED_UA = `OpenClawBot/${require("../../package.json").version}`;
const originalSourceFetch = sourceFetch;
const stampedSourceFetch = (input, init) => {
  const next = { ...(init || {}) };
  const h = new Headers(next.headers || {});
  h.set('User-Agent', FORCED_UA);
  h.delete('Sec-Fetch-Mode');
  if (h.get('Accept-Language') === '*') h.delete('Accept-Language');
  next.headers = h;
  return originalSourceFetch(input, next);
};
// then use stampedSourceFetch in place of sourceFetch below
RAW_BUFFERClick to expand / collapse

Symptom

Telegram outbound messages from OC get rate-limited / throttled by some ISPs after a sustained run, while a similarly-configured bun-based Telegram plugin running on the same machine has zero throttle problems.

Concretely:

  • OC sends Telegram API requests with User-Agent: undici (no version) — the Node.js global fetch default
  • A bun-based plugin sends User-Agent: Bun/1.3.12 and does not get throttled
  • Smoking-gun differentiator was UA. JA3 TLS fingerprint is a contributor but UA is dominant: it's a low-cardinality, easily-fingerprinted string that some consumer-ISP layer-7 inspection rules block aggressively.

Diagnosis

The OC Telegram extension uses grammy + undici 8.2.0. When resolveTelegramFetch (in extensions/telegram/src/fetch.ts, built to dist/fetch-*.js's resolveTelegramTransport / resolveTelegramFetch) wraps the global fetch, it never overrides the User-Agent header. undici's default User-Agent: undici flows out on every request.

The relevant build artifact in the installed package (v2026.5.10-beta.1):

  • dist/fetch-DBz2-saB.js:441-628resolveTelegramTransport / resolveTelegramFetch
  • dist/send-C7GFnTds.js:485-503resolveTelegramClientOptions passes the wrapped fetch into the grammy Bot client

Reproduction

  1. Run OC's Telegram channel for an extended period on an ISP that does layer-7 UA-based throttling (in our case a consumer UK ISP)
  2. After 1-N hours, getUpdates / sendMessage requests start returning network errors / hang, while the same machine's bun-based Telegram plugin keeps flowing
  3. Capture outbound TLS to api.telegram.org — UA in the request is bare undici

Suggested fix

In resolveTelegramTransport, wrap the returned fetch so every outbound request forces:

init.headers['User-Agent'] = `OpenClawBot/${pkgVersion}`;

(or any innocuous versioned UA, e.g. mimic the bun client's Bun/<version> for parity).

Optionally also strip the obvious "Node fetch" giveaway headers when present:

  • Sec-Fetch-Mode
  • Accept-Language: *

Implementation sketch (inside resolveTelegramTransport, after const sourceFetch = ... is computed):

const FORCED_UA = `OpenClawBot/${require("../../package.json").version}`;
const originalSourceFetch = sourceFetch;
const stampedSourceFetch = (input, init) => {
  const next = { ...(init || {}) };
  const h = new Headers(next.headers || {});
  h.set('User-Agent', FORCED_UA);
  h.delete('Sec-Fetch-Mode');
  if (h.get('Accept-Language') === '*') h.delete('Accept-Language');
  next.headers = h;
  return originalSourceFetch(input, next);
};
// then use stampedSourceFetch in place of sourceFetch below

Both sourceFetch direct calls and the per-attempt sourceFetch(input, withDispatcherIfMissing(...)) path need to go through the stamped wrapper.

Workaround for now

We're shipping a local patch via a per-install patcher script that idempotently sed-injects the UA-forcing wrapper into the built dist after each OC install/update. Happy to upstream a proper PR if there's interest — wanted to file the issue first so the design constraints (versioned UA, no header-strip surprises for users with custom apiRoot proxies, etc.) can be discussed.

Environment

  • OC v2026.5.10-beta.1
  • Node v22.22.2
  • macOS arm64
  • ISP: UK consumer broadband with layer-7 UA inspection

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