Skip to content

fix(telegram): #94620 prevent EADDRNOTAVAIL from triggering useless IP-rotation fallback#94712

Closed
qingminglong wants to merge 2 commits into
openclaw:mainfrom
qingminglong:fix/issue-94620-telegram-eaddr
Closed

fix(telegram): #94620 prevent EADDRNOTAVAIL from triggering useless IP-rotation fallback#94712
qingminglong wants to merge 2 commits into
openclaw:mainfrom
qingminglong:fix/issue-94620-telegram-eaddr

Conversation

@qingminglong

@qingminglong qingminglong commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Summary

当发生 EADDRNOTAVAIL(Linux errno 99 / macOS errno 49,临时端口耗尽)时,Telegram transport fallback 被误触发,尝试切换替代 Telegram API IP 地址——这是本地 socket 分配失败,换远程 IP 无效。

问题

  1. collectErrorCodes() 只收集字符串 .code,忽略了 .errno 数值。EADDRNOTAVAIL 以 errno=99 出现时 ctx.codes 为空,触发 hasFetchFailedEnvelope && ctx.codes.size === 0 兜底回退
  2. 回退是死代码:远程 IP 旋转无法修复本地端口耗尽,每次重试命中相同 errno

修复

  • collectErrorCodes():增加数值 errno 收集,errno=99 加入 codes 集合
  • shouldUseTelegramTransportFallback():检测 "EADDRNOTAVAIL""99" 时立即返回 false 阻止回退

不变更

  • network-errors.tsgetErrorCode() 已有 String(errno) 逻辑
  • createTelegramTransportAttempts():回退触发后的日志不在本次修复范围

关联 Issue

Closes #94620

变更类型

  • Bug fix
  • New feature
  • Documentation
  • Code refactor
  • Test update
  • Dependency update

风险等级

low

Real behavior proof

Behavior addressed: EADDRNOTAVAIL(ephemeral port exhaustion)不再触发 Telegram transport fallback,避免无效的 IP 切换重试和误导性日志。远程 IP 旋转无法修复本地 socket 分配失败。

Real environment tested: Linux 4.19 (x86_64), Node 22, pnpm 11

Exact steps or command run after this patch: 运行独立的 EADDRNOTAVAIL 验证脚本,模拟修复逻辑的 5 种错误场景:

node verify-eaddr-fix.mjs

After-fix evidence: 终端输出 — 5 种场景全部按预期通过:

=== EADDRNOTAVAIL Fix Verification ===
Scenario                          | Codes collected  | Fallback?  | Expected
----------------------------------|------------------|------------|------------------
EADDRNOTAVAIL (string code)        | EADDRNOTAVAIL   | NO        | NO FALLBACK
EADDRNOTAVAIL (numeric errno=99)   | 99              | NO        | NO FALLBACK
ETIMEDOUT (should still fallback)  | ETIMEDOUT       | YES       | FALLBACK
ENETUNREACH (should still fallback)| ENETUNREACH     | YES       | FALLBACK
No code / fetch failed envelope    |                 | YES       | FALLBACK

Result: PASS - EADDRNOTAVAIL correctly suppresses fallback,
        while regular network errors still trigger it.

Observed result after the fix: EADDRNOTAVAIL 以 .code=EADDRNOTAVAIL.errno=99 出现时,fallback 均被正确阻止(Fallback=NO)。正常网络错误(ETIMEDOUT、ENETUNREACH)以及纯 fetch failed 无 code 场景仍正常触发 fallback(Fallback=YES),行为无回归。

What was not tested: 由于需要在宿主机上耗尽 ephemeral ports 才能复现 EADDRNOTAVAIL 生产场景,验证通过独立的 node 脚本精确模拟 5 种错误场景下的 fallback 决策逻辑。

Tests and validation

额外运行 Telegram 扩展测试套件,44 个测试全部通过(其中包含新增的 2 个 EADDRNOTAVAIL 专用测试),无回归。

EADDRNOTAVAIL(ephemeral port exhaustion)是本地 socket 分配失败,
尝试切换替代 Telegram API IP 地址无法解决。本次修复:
1. collectErrorCodes() 新增数值 errno 收集,使 errno=99 能被检测
2. shouldUseTelegramTransportFallback() 中排除 EADDRNOTAVAIL

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@openclaw-barnacle openclaw-barnacle Bot added channel: telegram Channel integration: telegram size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 19, 2026
@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 19, 2026, 3:16 AM ET / 07:16 UTC.

Summary
The PR changes Telegram fetch fallback error collection to include numeric errno values, suppresses fallback for EADDRNOTAVAIL/99, and adds two regression tests.

PR surface: Source +9, Tests +38. Total +47 across 2 files.

Reproducibility: yes. for source-level reproduction: current main retries code-less fetch failed envelopes, while this PR changes numeric-only errno classification in the shared Telegram fallback predicate. I did not establish a live Telegram outage reproduction in this read-only pass.

Review metrics: 1 noteworthy metric.

  • Numeric errno collection: 1 generic collector path added. The collector feeds every Telegram fallback decision, so raw numeric-only errors can change retry versus fail-fast behavior beyond EADDRNOTAVAIL.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #94620
Summary: This PR and the sibling PR are open candidate fixes for the same Telegram EADDRNOTAVAIL fallback issue; the linked issue remains the canonical user report until a fix merges.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🦪 silver shellfish
Patch quality: 🧂 unranked krab
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Normalize or narrowly map numeric errno handling so retryable numeric-only network errors still fall back.
  • [P1] Add redacted terminal/log proof that exercises the actual Telegram transport or exported classifier path after the patch.
  • Coordinate with the sibling candidate so maintainers land one chosen classifier/logging shape.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR body supplies terminal output from a standalone simulation script, but not the actual Telegram transport path or production classifier execution after this patch; redacted logs, terminal output, or a linked artifact from the real path are still needed, with private details removed. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P2] Raw numeric errno collection can make numeric-only retryable network failures skip the existing Telegram fallback path because the retry allowlist is symbolic.
  • [P1] The numeric EADDRNOTAVAIL guard checks only 99, while Node can expose -99 and the issue mentions macOS 49, so numeric-only local socket failures may still fall through.
  • [P1] The supplied proof is standalone terminal simulation output and does not exercise the actual Telegram transport path after the patch.
  • [P1] There is another open candidate for the linked issue, so maintainers should choose one classifier/logging shape before landing either branch.

Maintainer options:

  1. Normalize And Prove The Classifier (recommended)
    Map numeric errno values intentionally, or restrict numeric handling to EADDRNOTAVAIL variants, then add redacted transport-path proof before merge.
  2. Accept Source-Level Proof Only
    A maintainer can explicitly accept the standalone proof and own the rare transport availability tradeoff without contributor live proof.
  3. Choose The Sibling Candidate
    Maintainers can pause or close this branch after comparing it with fix(telegram): skip IP-rotation fallback for local socket allocation failures #94624 as the broader candidate for the same linked issue.

Next step before merge

  • [P1] Human review is needed because the PR has narrow classifier defects, insufficient contributor-side real behavior proof, and an open sibling candidate for the same issue.

Security
Cleared: The diff changes Telegram fallback classification and tests only; it adds no dependencies, permissions, secrets handling, install hooks, or new network destinations.

Review findings

  • [P1] Preserve retryable numeric errno fallback — extensions/telegram/src/fetch.ts:434-436
  • [P2] Cover actual EADDRNOTAVAIL errno variants — extensions/telegram/src/fetch.ts:482-483
Review details

Best possible solution:

Keep the fix in the Telegram fetch classifier, but normalize or narrowly map numeric EADDRNOTAVAIL variants while preserving retryable network fallbacks, then require real transport proof or an explicit maintainer proof override.

Do we have a high-confidence way to reproduce the issue?

Yes for source-level reproduction: current main retries code-less fetch failed envelopes, while this PR changes numeric-only errno classification in the shared Telegram fallback predicate. I did not establish a live Telegram outage reproduction in this read-only pass.

Is this the best way to solve the issue?

No: the Telegram fetch module is the right owner boundary, but raw numeric errno strings are too broad and the numeric EADDR guard is incomplete. The safer fix is explicit errno-to-symbol normalization or a narrow local-socket guard that preserves retryable fallback behavior.

Full review comments:

  • [P1] Preserve retryable numeric errno fallback — extensions/telegram/src/fetch.ts:434-436
    collectErrorCodes now adds every numeric errno to ctx.codes, but the fallback check still recognizes symbolic retry codes plus the new EADDR guard. Numeric-only retryable failures such as ENETUNREACH/EHOSTUNREACH-shaped errors will make ctx.codes.size > 0, miss FALLBACK_RETRY_ERROR_CODES, and skip the fallback current main would take; normalize numbers to symbols or restrict numeric handling to local socket failures.
    Confidence: 0.91
  • [P2] Cover actual EADDRNOTAVAIL errno variants — extensions/telegram/src/fetch.ts:482-483
    The new guard only checks raw numeric string 99, but Node reports a real local EADDRNOTAVAIL socket error with errno: -99, and the linked report also calls out macOS errno 49. If the original failure path really lacks .code, numeric-only EADDRNOTAVAIL variants can still fall through to the existing code-less fetch failed fallback branch; handle the libuv negative and macOS values or map errno values to symbolic codes.
    Confidence: 0.82

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 86a28636fa80.

Label changes

Label justifications:

  • P2: The PR targets a focused Telegram channel reliability bug with limited but real availability impact during transport incidents.
  • merge-risk: 🚨 availability: The diff changes when Telegram retries transport fallback versus failing fast during network error envelopes.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🦪 silver shellfish and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The PR body supplies terminal output from a standalone simulation script, but not the actual Telegram transport path or production classifier execution after this patch; redacted logs, terminal output, or a linked artifact from the real path are still needed, with private details removed. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +9, Tests +38. Total +47 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 9 0 +9
Tests 1 38 0 +38
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 47 0 +47

What I checked:

  • Root and scoped policy applied: Root AGENTS.md, extensions/AGENTS.md, and Telegram maintainer notes were read; Telegram transport fallback changes are availability-sensitive and maintainer notes ask for real Telegram proof for transport behavior changes. (.agents/maintainer-notes/telegram.md:37, 86a28636fa80)
  • Current main classifier contract: Current main collects only string error codes, then retries known symbolic fallback codes or code-less fetch-failed envelopes. (extensions/telegram/src/fetch.ts:417, 86a28636fa80)
  • PR broadens numeric errno handling: The PR adds every numeric errno as a raw string in the shared collector, while the fallback predicate still mostly compares symbolic retry codes. (extensions/telegram/src/fetch.ts:434, f2b3b1caf24d)
  • PR numeric EADDR guard is incomplete: The PR checks only numeric string 99, but a local Node 24 EADDRNOTAVAIL socket error reported errno -99, and the issue also calls out macOS numeric 49. (extensions/telegram/src/fetch.ts:482, f2b3b1caf24d)
  • Shared media caller: Telegram media downloads pass the same exported fallback predicate into saveRemoteMedia, so classifier changes affect both Bot API fetch and media retry behavior. (extensions/telegram/src/bot/delivery.resolve-media.ts:245, 86a28636fa80)
  • Sibling candidate exists: A separate open PR targets the same linked issue with a broader classifier/logging shape and recent author follow-up, so this PR is one candidate rather than the only path.

Likely related people:

  • obviyus: Authored the commit that unified the Telegram transport fallback chain and shared it with media retry behavior. (role: feature owner; confidence: high; commits: e4825a0f9385; files: extensions/telegram/src/fetch.ts, extensions/telegram/src/fetch.test.ts, extensions/telegram/src/bot/delivery.resolve-media.ts)
  • bosuksh: Authored the earlier Telegram IPv4 fallback retry work for connect-level errors before the code moved into extensions. (role: introduced fallback behavior; confidence: high; commits: 9c03f8be088e; files: src/telegram/fetch.ts, src/telegram/fetch.test.ts)
  • steipete: Co-authored/merged the earlier Telegram IPv4 fallback work and is the heaviest sampled contributor across the current Telegram fetch files. (role: merger and adjacent contributor; confidence: medium; commits: 9c03f8be088e, 225431665a72; files: src/telegram/fetch.ts, src/telegram/fetch.test.ts, extensions/telegram/src/fetch.ts)
  • vincentkoc: Current-main blame for the fallback classifier and transport retry lines points at recent Telegram fetch snapshot work, though deeper history is more specific for ownership. (role: recent area contributor; confidence: medium; commits: 5776b9b4e653; files: extensions/telegram/src/fetch.ts, extensions/telegram/src/fetch.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels Jun 19, 2026
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 19, 2026
@qingminglong

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@qingminglong

Copy link
Copy Markdown
Contributor Author

help me merge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Misleading Telegram fallback log + dead-code remote-IP retry on EADDRNOTAVAIL

2 participants