OpenClaw duplicate messages: fix idempotency gaps and stop sending twice
Problem statement: your OpenClaw gateway sends the same message multiple times to Telegram, Slack, or other channels. Users report seeing duplicate assistant responses, your session files show repeated role=user messages after overflow recovery, and delivery-mirror operations create duplicate sends for every message action. The platform looks unreliable, but the root cause is usually missing idempotency protection.
- GitHub issue #66443 (created 2026-04-08): overflow recovery duplicates role=user messages in session JSONL.
- GitHub issue #64038 (created 2026-03-22): Telegram sendMessage retried without idempotency key causes duplicate messages.
- GitHub issue #65701 (created 2026-04-05): Telegram provider sends duplicate messages for every response.
- GitHub issue #65493 (created 2026-04-02): delivery-mirror duplicates messages sent via message(action=send).
Why duplicate messages damage trust
When an AI assistant sends the same response twice, users notice immediately. In group chats, duplicates create confusion about which message to reply to. In direct messages, they feel like a bug or malfunction. Over time, repeated duplicates erode confidence in the entire platform. Teams start adding manual checks and workarounds, which slows down operations and creates technical debt.
The operational cost is also real. Duplicate messages consume API quota, waste token spend, and pollute logs with redundant delivery events. When debugging incidents, engineers must distinguish between genuine duplicates caused by idempotency gaps and legitimate retry behavior. This ambiguity increases incident response time and makes root cause analysis harder.
Idempotency explained: the key that prevents duplicates
Idempotency is a simple concept with powerful effects. An idempotent operation is one that can be applied multiple times with the same result as applying it once. For message delivery, this means that retrying a send operation should not create multiple copies of the same message.
The standard implementation pattern uses an idempotency key: a unique identifier generated by the client and included with the original send request. When the gateway retries after a transient failure, it includes the same key. The destination platform checks whether a message with that key was already processed, and if so, returns the original result without creating a duplicate.
Without idempotency keys, every retry is treated as a new message. Network timeouts, gateway restarts, and temporary service unavailability all trigger retries. In a busy OpenClaw deployment, these conditions occur regularly. Idempotency protection is not optional for production systems.
Common sources of duplicate messages
1) Telegram sendMessage without idempotency key
The Telegram Bot API supports idempotency through request identifiers. When sendMessage is called after a transient failure without including the original request ID, Telegram creates a new message instead of recognizing the retry. This is the most common cause of duplicate assistant responses in Telegram channels.
The issue appears most often during gateway restarts, network blips, or when Telegram returns temporary errors like 429 rate limit responses. If the OpenClaw Telegram provider does not preserve and replay the original request identifier, each retry becomes a separate message.
2) Overflow recovery replay without deduplication
OpenClaw uses overflow recovery to handle session files that exceed token limits. During recovery, the system reconstructs session state by replaying messages from the session JSONL file. If this replay logic does not include deduplication guards, the same role=user messages can appear multiple times in the recovered session.
This issue is particularly insidious because it corrupts the session file itself. Users see their own prompts repeated in conversation history, and downstream tools that consume session state may process the same user input multiple times. The problem compounds across recovery cycles if the root cause is not addressed.
3) Delivery-mirror duplicate sends
The delivery-mirror feature routes messages through multiple delivery paths for redundancy or observability. When message(action=send) is mirrored without idempotency tracking, each mirror path creates its own copy of the message. This is especially problematic for channels like Slack and WhatsApp where duplicates are highly visible to users.
Delivery-mirror duplicates often indicate that the mirror layer treats each path as independent without coordinating through a central deduplication store. The fix requires either a shared idempotency cache or conditional mirror logic that skips already-delivered messages.
4) Email notification replays
OpenClaw sends transactional emails for events like subscription activation and instance readiness. Without persistent deduplication markers, webhook replays or task retries can trigger multiple email sends for the same event. Users receive duplicate notifications, which looks unprofessional and creates support overhead.
First-party worklogs from 2026-04-09 document the implementation of email dedupe guardrails using persistent markers in the billing_account and instance tables. These markers track whether a notification email was already sent for a specific event, preventing duplicates even when webhooks replay.
Full diagnostic and fix playbook
1) Identify the duplicate source
Start by gathering evidence. Capture exact timestamps, message IDs, channel details, and gateway logs for several duplicate events. Look for patterns: are duplicates associated with gateway restarts, specific channels, or overflow recovery cycles? The pattern points to the root cause.
2) Check Telegram provider idempotency configuration
Review your Telegram provider configuration in the gateway settings. Confirm that sendMessage calls include request identifiers and that these identifiers are preserved across retries. If your deployment uses a custom Telegram integration, verify the implementation matches the Bot API idempotency specification.
3) Inspect overflow recovery logic
Examine your session files after an overflow recovery event. Look for repeated role=user entries with identical or near-identical timestamps. Check the gateway's overflow recovery code for deduplication guards. If guards are missing, implement a message fingerprint check before replaying each entry.
4) Review delivery-mirror configuration
If you use delivery-mirror, audit which channels are mirrored and whether duplicate sends occur only on mirrored paths. Test with mirroring disabled to confirm the correlation. If confirmed, either implement shared idempotency tracking for all mirror paths or disable mirroring for channels where duplicates are unacceptable.
5) Add persistent deduplication markers
For email notifications and other one-shot events, add database markers that record whether a specific event was already processed. The marker should include a unique event identifier (such as subscription ID for billing emails) and a timestamp. Check this marker before processing and update it atomically to prevent race conditions.
6) Implement webhook idempotency
For Stripe and other webhook-based integrations, maintain a claim table of processed webhook event IDs. When a webhook arrives, first check whether its event ID was already processed. If so, acknowledge the webhook without taking action. This prevents duplicate processing when webhooks are redelivered.
7) Add observability for duplicate events
Create metrics and alerts for duplicate detection. Track duplicate send attempts, successful idempotency handling, and overflow recovery deduplication passes. Alert when duplicate rates exceed thresholds. This visibility helps you catch idempotency gaps before users report them.
8) Test idempotency under failure conditions
Don't assume idempotency works—prove it. In a staging environment, trigger transient failures (network timeouts, temporary service unavailability) and verify that retries do not create duplicates. Test overflow recovery with sessions that will trigger compaction. Confirm that email and webhook idempotency handles replay events correctly.
Prevention strategies for production deployments
Design idempotency into all send operations
Every message send operation should include an idempotency key by default, not as an optional feature. Design your provider integrations to generate and preserve these keys automatically. Make idempotency part of the standard send path rather than a special case.
Use persistent stores for deduplication state
In-memory deduplication caches do not survive gateway restarts. Use database tables or persistent caches for idempotency markers. This ensures that deduplication state survives process restarts and scales across multiple gateway instances.
Implement row-level locking for critical operations
For operations like email sending that must happen exactly once, use database row locks (SELECT FOR UPDATE) to prevent race conditions. This ensures that even if multiple workers process the same trigger event simultaneously, only one succeeds in sending the notification.
Centralize idempotency logic
Avoid scattering idempotency checks across multiple code paths. Create a centralized idempotency service or library that all send operations call. This reduces the chance of implementation gaps and makes auditing easier.
Edge cases that catch teams off guard
- Partial message sends: a send operation times out after partially committing state but before confirming delivery.
- Cascading retries: one provider's retry triggers retries in downstream providers, multiplying duplicates.
- Session export/import: importing a session with duplicate entries contaminates the new instance.
- Time synchronization: clock drift between gateway instances causes timestamp-based deduplication to fail.
- Idempotency key collisions: insufficient key entropy causes unrelated messages to share the same key.
Verification checklist before declaring victory
- No duplicate Telegram messages observed after simulating transient failures.
- Overflow recovery produces clean session files without repeated role=user entries.
- Delivery-mirror paths coordinate through shared idempotency state.
- Email notifications are sent exactly once per event even with webhook replays.
- Metrics show zero duplicate send attempts over a 24-hour production window.
- Stress tests with intentional failures confirm idempotency under load.
Fix once. Stop recurring duplicate message incidents.
If this keeps coming back, you can move your existing setup to managed OpenClaw cloud hosting instead of rebuilding the same stack. Import your current instance, keep your context, and move onto a runtime with lower ops overhead.
- Import flow in ~1 minute
- Keep your current instance context
- Run with managed security and reliability defaults
If you would rather compare options first, review OpenClaw cloud hosting or see the best OpenClaw hosting options before deciding.
If your team spends time tracking down duplicate message causes instead of building features, move your current runtime into managed operations with built-in idempotency protection.
Import your current OpenClaw instance in 1 click Review self-hosted setup checklist
FAQ
Will adding idempotency keys slow down message delivery?
No. Idempotency checks add minimal overhead—a single database lookup or cache query per send. The performance cost is negligible compared to the cost of duplicate sends, retries, and user confusion. In fact, proper idempotency can improve performance by reducing unnecessary retry traffic.
Do all OpenClaw providers support idempotency?
Not all external providers offer native idempotency APIs. For providers that don't, you must implement application-level deduplication using persistent state stores. The pattern is the same: generate a unique identifier for each send, check if it was already processed, and skip if so.
Can I rely on message timestamps for deduplication?
Timestamps alone are insufficient for deduplication because multiple sends can occur within the same timestamp resolution, especially under retry conditions. Use explicit idempotency keys or content hashes combined with timestamps for reliable deduplication.
Should I disable retries to avoid duplicates?
No. Retries are essential for handling transient failures. The solution is not to disable retries but to make them idempotent. Keep retry logic for resilience, but add idempotency keys so that retries don't create duplicates.