Skip to content

fix(telegram): durably retry inbound media dropped during restart (#98076)#98102

Merged
obviyus merged 3 commits into
openclaw:mainfrom
luoyanglang:wolf/telegram-media-fetch-retryable-on-restart
Jul 1, 2026
Merged

fix(telegram): durably retry inbound media dropped during restart (#98076)#98102
obviyus merged 3 commits into
openclaw:mainfrom
luoyanglang:wolf/telegram-media-fetch-retryable-on-restart

Conversation

@luoyanglang

@luoyanglang luoyanglang commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Fixes #98076

What Problem This Solves

A single inbound media message (e.g. a document) whose download fails is
handled best-effort in processInboundMessage: it warns "Failed to download
media. Please try again." and lets the update complete. bot-core then marks
the spooled update completed: true (acked). During a gateway
restart/upgrade window the warning send also fails, so the document is
silently dropped — never written to media/inbound/, no retry, no notice to
the user or the assistant. The issue confirms a re-send after the restart landed
correctly, so the loss is restart-window-specific.

Why This Change Was Made

Inbound media that fails transiently around a restart should be durably re-driven
after the gateway comes back, not acked-and-lost. The spooled-replay completion
contract already keeps failed-retryable updates; the single-message media catch
just never recorded that outcome, so the spool acked the update and dropped it.

The retry decision must be precise. fetch_failed is the catch-all media error
code and covers both transient failures (network, shutdown/abort) and
permanent ones (SSRF/guard denials, local Bot API path/read errors). Durably
retrying a permanent failure would loop it in the spool with the user warning
suppressed. The correct transient/abort determination already lives in
src/media/fetch.ts (isAbortError / isTransientNetworkError), so the policy
belongs there rather than re-derived in the channel.

User Impact

  • A document sent right before a gateway restart is now downloaded after the
    gateway comes back
    instead of silently lost.
  • No new user-facing warning spam: the "try again" notice is suppressed only on
    durable spooled replay (we are retrying); live failures and permanent failures
    still warn exactly as before.
  • Permanent failures (size limit, blocked/guarded fetch, bad path) are not
    requeued — they stay best-effort (acked + warned) and cannot loop the spool.
  • Media-group albums are unchanged: they keep their intentional
    skip-the-unfetchable-photo + partial-delivery warning ([Bug] Telegram media group: silent image loss when one photo fails to download #55216).

Evidence

Source-level regression proof, run locally on macOS against this patch (this issue
is needs-live-repro; a live restart-during-upload rig and the agentic native
Telegram proof job require maintainer/write perms, so the mechanism is proven
against the real handler + bot-core completion frame):

  • src/media/fetch.tsisDurablyRetryableMediaFetchError =
    shouldRetryMediaFetch (the canonical in-loop transient policy) plus
    shutdown/abort fetch_failed. Truth-table unit tests in
    src/media/fetch.test.ts cover: transient-network + 408/5xx → retry;
    shutdown/abort fetch_failed → retry; permanent fetch_failed (guard/SSRF,
    local path), other 4xx, and size limits → no retry
    .
  • Behavioral tests in bot.create-telegram-bot.channel-post-media.test.ts
    drive a real single-message document fetch failure through the actual handler
    inside runWithTelegramUpdateProcessingFrame + withTelegramSpooledReplayUpdate:
    • spooled-replay shutdown abort{ kind: "failed-retryable" } (kept) and
      the retry notice is not sent;
    • spooled-replay permanent fetch_failed (guard/SSRF) → acked (no
      failed-retryable result) and the user is warned;
    • spooled-replay size-limit → acked + warned.
  • Full telegram + media suites green (fetch, bot-core,
    bot-update-tracker, polling-session, bot-message, media-group
    skip-warning, classifier + behavioral): 264 tests pass.
  • pnpm tsgo:core + tsgo:core:test + tsgo:extensions +
    tsgo:extensions:test, pnpm plugin-sdk:api:check, oxfmt --check,
    node scripts/run-oxlint.mjs, and staged gitleaks protect --staged --redact
    all clean.

Not Covered

A live Telegram account with a gateway restart triggered mid-upload is not
reproduced here; the durable-keep / ack / warning-suppression behavior is asserted
at the source level. A maintainer can capture visible proof via Mantis if desired.

@openclaw-barnacle openclaw-barnacle Bot added channel: telegram Channel integration: telegram size: S triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. labels Jun 30, 2026
@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 1, 2026, 4:00 PM ET / 20:00 UTC.

Summary
The PR changes Telegram single-message media handling to classify transient media fetch failures as failed-retryable during spooled replay, suppress replay warning spam, add regression tests, and still leaves a stale generated SDK baseline hash file.

PR surface: Source +36, Tests +173, Generated 0. Total +209 across 5 files.

Reproducibility: yes. source-level only: current main catches single-message media fetch failure, sends a best-effort warning, releases dedupe keys, and returns while bot-core completes the update unless failed-retryable is recorded. A live Telegram restart-window reproduction is still missing.

Review metrics: 1 noteworthy metric.

  • Generated SDK Baseline State: 1 stale generated hash file changed; 0 SDK export or budget changes remain. The remaining generated baseline diff no longer matches the Telegram-local implementation and contributes to the dirty merge state.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #98076
Summary: This PR is the candidate fix for the canonical Telegram restart-window document-loss issue; broader restart durability and spooled-lane work overlaps but does not replace it.

Members:

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

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦐 gold shrimp
Result: blocked until 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:

  • [P1] Add redacted live Telegram restart proof showing the document retries after restart, the warning does not repeat on replay, and the spool drains.
  • Rebase or revert the stale generated SDK baseline hash file so the PR matches the Telegram-local implementation.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body provides source-level tests and local check claims, but no after-fix live Telegram restart proof from a real setup; after adding redacted proof, updating the PR body should trigger a fresh review. 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.

Mantis proof suggestion
A native Telegram proof would materially show the restart-window document retry and absence of repeated failure notices. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis telegram desktop proof: send a Telegram document during a gateway restart and verify it retries after restart without repeated failure notices.

Risk before merge

  • [P1] Real Telegram restart-window proof is still missing, so source-level tests do not yet show a document actually retries after a gateway restart and the spool drains without repeated failure notices.
  • [P1] The branch is currently CONFLICTING/DIRTY and leaves a stale generated SDK baseline hash change after the SDK export path was removed.
  • [P1] The ack-versus-requeue change is delivery-sensitive: a wrong retry classification can either silently ack lost media or keep replaying an update while suppressing warnings.
  • [P1] The linked issue discussion includes alternate restart/spool-claim loss windows, so live proof should confirm this PR covers the observed document-loss path.

Maintainer options:

  1. Prove And Clean The Retry Fix (recommended)
    Before merge, rebase or revert the stale SDK baseline hash and add redacted live Telegram restart proof showing document retry, no repeated warning spam, and a drained spool.
  2. Accept Source-Level Proof Only
    Maintainers could intentionally accept the source-level handler tests without live proof, but that owns the risk that the real restart window is a different spool path.
  3. Pause For Mantis Proof
    If contributor-side live proof is unavailable, pause the PR until a maintainer or Mantis lane captures the Telegram restart-window behavior.

Next step before merge

  • [P1] Manual review remains because the PR needs real Telegram proof plus branch/baseline cleanup before merge; automation cannot provide the contributor's live setup proof.

Security
Cleared: No dependency, workflow, secret, package-resolution, or SSRF-guard regression was found; permanent guard-style fetch failures remain non-retryable.

Review findings

  • [P2] Revert the stale SDK baseline hashes — docs/.generated/plugin-sdk-api-baseline.sha256:1-2
Review details

Best possible solution:

Land a rebased Telegram-local retry fix that reverts the stale generated baseline, keeps the linked issue open until merge, and includes redacted live Telegram restart proof for the document retry path.

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

Yes, source-level only: current main catches single-message media fetch failure, sends a best-effort warning, releases dedupe keys, and returns while bot-core completes the update unless failed-retryable is recorded. A live Telegram restart-window reproduction is still missing.

Is this the best way to solve the issue?

Mostly yes for owner boundary: the latest head keeps the retry policy Telegram-local and uses the existing failed-retryable spooled replay contract. It is not merge-ready until the stale generated baseline is reverted and real restart proof confirms the observed loss path.

Full review comments:

  • [P2] Revert the stale SDK baseline hashes — docs/.generated/plugin-sdk-api-baseline.sha256:1-2
    The final head moved the retry policy back into the Telegram plugin and reverted the SDK surface budget, but this generated hash file still contains the earlier SDK-export baseline values. Since the PR no longer changes any SDK export, leaving these hashes changed creates a stale baseline diff and contributes to the current conflict with main; please revert this file with the SDK export removal.
    Confidence: 0.86

Overall correctness: patch is incorrect
Overall confidence: 0.86

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 44cb3f7b980f.

Label changes

Label justifications:

  • P1: The PR is a candidate fix for silent Telegram document loss during gateway restart, which breaks a real channel workflow.
  • merge-risk: 🚨 message-delivery: The diff changes whether failed Telegram media updates are acknowledged or requeued, which can affect inbound media delivery.
  • merge-risk: 🚨 availability: A bad durable retry classification could leave Telegram spool work pending or repeatedly replaying instead of draining cleanly.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body provides source-level tests and local check claims, but no after-fix live Telegram restart proof from a real setup; after adding redacted proof, updating the PR body should trigger a fresh review. 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.
  • mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. The PR changes visible Telegram document delivery and failure-notice behavior, which a short Telegram Desktop proof can demonstrate.
Evidence reviewed

PR surface:

Source +36, Tests +173, Generated 0. Total +209 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 2 48 12 +36
Tests 2 173 0 +173
Docs 0 0 0 0
Config 0 0 0 0
Generated 1 2 2 0
Other 0 0 0 0
Total 5 223 14 +209

What I checked:

Likely related people:

  • joshavant: Authored merged Telegram spooled replay and claim-refresh work that defines the failed-retryable replay and queue lifecycle this PR relies on. (role: recent area contributor; confidence: high; commits: 9921825e1795, db255b1154c1; files: extensions/telegram/src/bot-core.ts, extensions/telegram/src/bot-processing-outcome.ts, extensions/telegram/src/polling-session.ts)
  • vincentkoc: Authored the merged stalled Telegram ingress spool claim recovery work that overlaps with the restart-window durability path. (role: recent area contributor; confidence: high; commits: b8e3de11608d; files: extensions/telegram/src/polling-session.ts, extensions/telegram/src/telegram-ingress-spool.ts, src/channels/message/ingress-queue.ts)
  • obviyus: Authored the latest PR-head policy-localizing commit and current-main generated baseline update, and carried earlier restart-era Telegram spool recovery into main. (role: recent area contributor; confidence: medium; commits: 0e762753d75a, 53fe2e4bc3ca, aedcf0f897d4; files: extensions/telegram/src/bot-handlers.media.ts, extensions/telegram/src/bot-handlers.runtime.ts, docs/.generated/plugin-sdk-api-baseline.sha256)
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.

@openclaw-barnacle openclaw-barnacle Bot removed the triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. label Jun 30, 2026
@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. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. labels Jun 30, 2026
luoyanglang added a commit to luoyanglang/openclaw that referenced this pull request Jun 30, 2026
openclaw#98076)

Review on openclaw#98102: the durable-retry classifier treated every MediaFetchError
`fetch_failed` as retryable, but `fetch_failed` also covers permanent failures
(SSRF/guard denials, local Bot API path/read errors). On spooled replay those
would requeue forever with the user-facing warning suppressed.

Move the policy to its owner: add `isDurablyRetryableMediaFetchError` to
`src/media/fetch.ts`, defined as `shouldRetryMediaFetch` (the canonical in-loop
transient policy) plus shutdown/abort `fetch_failed`. A restart-window abort is
the primary inbound-media loss vector and is recoverable on replay, whereas
in-loop retry mid-shutdown is futile; permanent `fetch_failed`, other 4xx, and
size limits stay non-retryable so they cannot loop in the spool.

Telegram reuses the shared helper instead of a local classifier
(`isAbortError`/`isTransientNetworkError` live in core; the classification must
not drift). Adds permanent-`fetch_failed` regression coverage at the unit and
behavioral level. Media-group partial delivery (openclaw#55216) unchanged.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation size: M and removed size: S labels Jun 30, 2026
@luoyanglang

Copy link
Copy Markdown
Contributor Author

Addressed the P2 finding on bot-handlers.media.ts (over-broad fetch_failed durable retry) in 720d1e9c6e.

The policy is moved to its owner and narrowed. New isDurablyRetryableMediaFetchError in src/media/fetch.ts is shouldRetryMediaFetch (the canonical in-loop transient policy) plus shutdown/abort fetch_failed:

  • transient network + 408/5xx HTTP + shutdown/abort → durably retried;
  • permanent fetch_failed (SSRF/guard denial, local Bot API path/read), other 4xx, and size limits → not retried, so they no longer loop in the spool with the warning suppressed.

Telegram now reuses the shared core helper instead of a local classifier, since isAbortError/isTransientNetworkError live in core and the classification must not drift. Added the requested permanent-fetch_failed coverage: a truth-table in src/media/fetch.test.ts and a behavioral spooled-replay test in bot.create-telegram-bot.channel-post-media.test.ts asserting a guard/SSRF fetch_failed is acked + warned (not requeued). Media-group partial delivery (#55216) is unchanged.

On proof: a live Telegram restart-during-upload rig and the agentic native Telegram proof job need maintainer/write perms I don't have as a contributor. The behavior is asserted at the source level against the real handler + bot-core completion frame; a maintainer can capture visible proof via Mantis if wanted.

@clawsweeper re-review

🤖 Addressed by Claude Code

@clawsweeper

clawsweeper Bot commented Jun 30, 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.

@clawsweeper clawsweeper Bot added the merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. label Jun 30, 2026
@luoyanglang luoyanglang reopened this Jun 30, 2026
@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts and removed scripts Repository scripts labels Jun 30, 2026
@clawsweeper clawsweeper Bot removed the merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. label Jul 1, 2026
luoyanglang and others added 3 commits July 1, 2026 12:59
…enclaw#98076)

A single-message inbound media fetch failure was handled best-effort: warn
and let the update complete, so bot-core acked the spooled update. During a
restart window the warning send also failed and the document was silently
lost with no durable retry.

Classify transient media fetch failures (network/abort fetch_failed and
408/429/5xx HTTP) and record them as failed-retryable, so spooled-replay
updates are kept and re-driven by the ingress spool with backoff instead of
acked. Live updates still ack and warn; permanent failures (size limit, 4xx)
stay best-effort. The retry notice is suppressed on spooled replay to avoid
repeating it on each replay.

The durable-retry classifier deliberately diverges from media/fetch.ts
shouldRetryMediaFetch on shutdown aborts: in-loop retry of a shutdown abort
is futile, but durable re-spool must retry it -- that abort is the primary
restart-window loss vector. Media-group partial delivery (openclaw#55216) is left
unchanged.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
openclaw#98076)

Review on openclaw#98102: the durable-retry classifier treated every MediaFetchError
`fetch_failed` as retryable, but `fetch_failed` also covers permanent failures
(SSRF/guard denials, local Bot API path/read errors). On spooled replay those
would requeue forever with the user-facing warning suppressed.

Move the policy to its owner: add `isDurablyRetryableMediaFetchError` to
`src/media/fetch.ts`, defined as `shouldRetryMediaFetch` (the canonical in-loop
transient policy) plus shutdown/abort `fetch_failed`. A restart-window abort is
the primary inbound-media loss vector and is recoverable on replay, whereas
in-loop retry mid-shutdown is futile; permanent `fetch_failed`, other 4xx, and
size limits stay non-retryable so they cannot loop in the spool.

Telegram reuses the shared helper instead of a local classifier
(`isAbortError`/`isTransientNetworkError` live in core; the classification must
not drift). Adds permanent-`fetch_failed` regression coverage at the unit and
behavioral level. Media-group partial delivery (openclaw#55216) unchanged.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@obviyus
obviyus force-pushed the wolf/telegram-media-fetch-retryable-on-restart branch from 0e76275 to 391f80f Compare July 1, 2026 20:03
@openclaw-barnacle openclaw-barnacle Bot removed the docs Improvements or additions to documentation label Jul 1, 2026
@obviyus
obviyus merged commit 44e88f5 into openclaw:main Jul 1, 2026
89 of 90 checks passed
obviyus pushed a commit that referenced this pull request Jul 1, 2026
#98076)

Review on #98102: the durable-retry classifier treated every MediaFetchError
`fetch_failed` as retryable, but `fetch_failed` also covers permanent failures
(SSRF/guard denials, local Bot API path/read errors). On spooled replay those
would requeue forever with the user-facing warning suppressed.

Move the policy to its owner: add `isDurablyRetryableMediaFetchError` to
`src/media/fetch.ts`, defined as `shouldRetryMediaFetch` (the canonical in-loop
transient policy) plus shutdown/abort `fetch_failed`. A restart-window abort is
the primary inbound-media loss vector and is recoverable on replay, whereas
in-loop retry mid-shutdown is futile; permanent `fetch_failed`, other 4xx, and
size limits stay non-retryable so they cannot loop in the spool.

Telegram reuses the shared helper instead of a local classifier
(`isAbortError`/`isTransientNetworkError` live in core; the classification must
not drift). Adds permanent-`fetch_failed` regression coverage at the unit and
behavioral level. Media-group partial delivery (#55216) unchanged.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@obviyus

obviyus commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Landed via rebase onto main.

  • Scoped tests: node scripts/run-vitest.mjs extensions/telegram/src/bot-handlers.media.test.ts extensions/telegram/src/bot.create-telegram-bot.channel-post-media.test.ts
  • Format/API checks: direct oxfmt --check, direct plugin-sdk-surface-report.mjs --check, direct generate-plugin-sdk-api-baseline.ts --check
  • Changelog: not updated; release generation owns CHANGELOG.md
  • Land commit: 391f80f
  • Merge commit: 44e88f5

Thanks @luoyanglang!

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 2, 2026
openclaw#98076)

Review on openclaw#98102: the durable-retry classifier treated every MediaFetchError
`fetch_failed` as retryable, but `fetch_failed` also covers permanent failures
(SSRF/guard denials, local Bot API path/read errors). On spooled replay those
would requeue forever with the user-facing warning suppressed.

Move the policy to its owner: add `isDurablyRetryableMediaFetchError` to
`src/media/fetch.ts`, defined as `shouldRetryMediaFetch` (the canonical in-loop
transient policy) plus shutdown/abort `fetch_failed`. A restart-window abort is
the primary inbound-media loss vector and is recoverable on replay, whereas
in-loop retry mid-shutdown is futile; permanent `fetch_failed`, other 4xx, and
size limits stay non-retryable so they cannot loop in the spool.

Telegram reuses the shared helper instead of a local classifier
(`isAbortError`/`isTransientNetworkError` live in core; the classification must
not drift). Adds permanent-`fetch_failed` regression coverage at the unit and
behavioral level. Media-group partial delivery (openclaw#55216) unchanged.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 3, 2026
openclaw#98076)

Review on openclaw#98102: the durable-retry classifier treated every MediaFetchError
`fetch_failed` as retryable, but `fetch_failed` also covers permanent failures
(SSRF/guard denials, local Bot API path/read errors). On spooled replay those
would requeue forever with the user-facing warning suppressed.

Move the policy to its owner: add `isDurablyRetryableMediaFetchError` to
`src/media/fetch.ts`, defined as `shouldRetryMediaFetch` (the canonical in-loop
transient policy) plus shutdown/abort `fetch_failed`. A restart-window abort is
the primary inbound-media loss vector and is recoverable on replay, whereas
in-loop retry mid-shutdown is futile; permanent `fetch_failed`, other 4xx, and
size limits stay non-retryable so they cannot loop in the spool.

Telegram reuses the shared helper instead of a local classifier
(`isAbortError`/`isTransientNetworkError` live in core; the classification must
not drift). Adds permanent-`fetch_failed` regression coverage at the unit and
behavioral level. Media-group partial delivery (openclaw#55216) unchanged.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@luoyanglang
luoyanglang deleted the wolf/telegram-media-fetch-retryable-on-restart branch July 5, 2026 08:14
sheyanmin pushed a commit to sheyanmin/openclaw that referenced this pull request Jul 8, 2026
openclaw#98076)

Review on openclaw#98102: the durable-retry classifier treated every MediaFetchError
`fetch_failed` as retryable, but `fetch_failed` also covers permanent failures
(SSRF/guard denials, local Bot API path/read errors). On spooled replay those
would requeue forever with the user-facing warning suppressed.

Move the policy to its owner: add `isDurablyRetryableMediaFetchError` to
`src/media/fetch.ts`, defined as `shouldRetryMediaFetch` (the canonical in-loop
transient policy) plus shutdown/abort `fetch_failed`. A restart-window abort is
the primary inbound-media loss vector and is recoverable on replay, whereas
in-loop retry mid-shutdown is futile; permanent `fetch_failed`, other 4xx, and
size limits stay non-retryable so they cannot loop in the spool.

Telegram reuses the shared helper instead of a local classifier
(`isAbortError`/`isTransientNetworkError` live in core; the classification must
not drift). Adds permanent-`fetch_failed` regression coverage at the unit and
behavioral level. Media-group partial delivery (openclaw#55216) unchanged.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: telegram Channel integration: telegram mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P1 High-priority user-facing bug, regression, or broken workflow. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: M 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.

[Bug]: Telegram inbound documents silently dropped if received during gateway restart/upgrade window

2 participants