openclaw - 💡(How to fix) Fix Telegram: old messages reprocessed in loop after gateway restart (update_id offset not persisted) [1 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#59331Fetched 2026-04-08 02:25:53
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Participants
Timeline (top)
cross-referenced ×1

When the OpenClaw gateway restarts (manually or after a crash), Telegram messages that were already processed get re-fetched and reprocessed. This causes duplicate responses — in one instance, the same message was processed 5 times in rapid succession.

Root Cause

Root Cause (suspected)

Fix Action

Workaround

We are currently running a pre-start script that calls getUpdates?offset=-1 before launching the gateway to flush stale updates. This works but is fragile and doesn't survive crash scenarios where the script doesn't run.

RAW_BUFFERClick to expand / collapse

Description

When the OpenClaw gateway restarts (manually or after a crash), Telegram messages that were already processed get re-fetched and reprocessed. This causes duplicate responses — in one instance, the same message was processed 5 times in rapid succession.

Root Cause (suspected)

The grammY long-polling runner tracks update_id offsets in memory. On gateway restart, this offset is lost, so the Bot API re-delivers all updates that haven't expired server-side (Telegram holds unconfirmed updates for ~24 hours).

Impact

  • Users receive duplicate replies (confusing, wastes API tokens)
  • Combined with long API completion timeouts, this creates cascading failures: multiple duplicate completions run simultaneously, exhaust concurrency slots, and can crash the gateway
  • Observed crash pattern on: 3/22, 3/26, 3/31, and 4/1

Reproduction

  1. Send a message to the bot via Telegram
  2. Restart the gateway (openclaw gateway restart)
  3. Observe the same message being processed again (visible in logs)
  4. If the restart was caused by a crash during completion, multiple stale messages replay simultaneously

Expected Behavior

  • The gateway should persist the last confirmed update_id offset to disk before shutdown
  • On startup, read the persisted offset and skip already-processed updates
  • As a defense-in-depth measure, track recently processed Telegram message_id values (ring buffer or TTL set) and skip duplicates even if the offset is stale

Suggested Implementation

  1. Persist update_id offset — Write to ~/.openclaw/telegram/poll-offset.json (or similar) after each successful batch. Read on startup.
  2. Dedup ring buffer — Keep a rolling set of the last N processed message_id values in memory. Skip any inbound message whose ID is already in the set.
  3. Graceful shutdown hook — Ensure the offset is flushed on SIGTERM/SIGINT before the process exits.

Workaround

We are currently running a pre-start script that calls getUpdates?offset=-1 before launching the gateway to flush stale updates. This works but is fragile and doesn't survive crash scenarios where the script doesn't run.

Environment

  • OpenClaw 2026.4.1 (da64a97)
  • macOS (arm64), Node v22.22.1
  • Telegram channel: long polling mode (default)
  • agents.defaults.maxConcurrent: 4

extent analysis

TL;DR

Implementing a persistent update_id offset storage and a deduplication mechanism can prevent duplicate message processing after gateway restarts.

Guidance

  • To prevent duplicate message processing, consider implementing the suggested update_id offset persistence to disk, as described in the issue, to ensure the gateway can resume from the last confirmed offset after a restart.
  • Implement a deduplication ring buffer to track recently processed message_id values and skip duplicates, providing an additional layer of defense against stale messages.
  • Ensure a graceful shutdown hook is in place to flush the offset to disk before the process exits, preventing data loss in case of a crash.
  • Review the current workaround using getUpdates?offset=-1 and consider replacing it with a more robust solution that can survive crash scenarios.

Example

// Simplified example of persisting update_id offset
const fs = require('fs');
const offsetFile = '~/.openclaw/telegram/poll-offset.json';

// Write update_id offset to file after each successful batch
function saveOffset(updateId) {
  fs.writeFileSync(offsetFile, JSON.stringify({ updateId }));
}

// Read persisted offset on startup
function loadOffset() {
  try {
    const data = fs.readFileSync(offsetFile, 'utf8');
    return JSON.parse(data).updateId;
  } catch (err) {
    return 0; // Default offset if file does not exist
  }
}

Notes

The provided example is a simplified illustration and may require adaptation to fit the specific requirements of the OpenClaw gateway and grammY long-polling runner.

Recommendation

Apply the suggested implementation of persisting update_id offset and deduplication mechanism to prevent duplicate message processing after gateway restarts, as it provides a more robust solution than the current workaround.

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