Bug
When an inbound message arrives while the agent is busy (long-running tool calls), the followup queue can deliver the same message to the agent multiple times across successive drain cycles.
Steps to reproduce
- Agent is busy with a long-running task (e.g., background exec generating files for ~30 minutes)
- User sends a single message during this time
- Message gets queued as expected (
[Queued messages while agent was busy])
- Agent receives the same message 2-3 times in separate
[Queued messages] blocks
Expected behavior
Each inbound message should be delivered to the agent exactly once, regardless of how many drain cycles occur while the agent is busy.
Root cause (analysis)
The inbound dedupe layer (shouldSkipDuplicateInbound in src/auto-reply/reply/inbound-dedupe.ts) correctly prevents duplicate inbound processing using a TTL cache keyed on MessageSid.
However, the followup queue dedupe (isRunAlreadyQueued in src/auto-reply/reply/queue/enqueue.ts) only checks against items currently in queue.items. When the queue drains via buildCollectPrompt, delivered items are spliced out (queue.items.splice(0, items.length)). If a subsequent drain cycle re-enqueues the same message, the dedupe check passes because the array no longer contains the previously delivered item.
Suggested fix
Apply a TTL-based dedupe cache (similar to inboundDedupeCache) to the followup queue drain. Track delivered message IDs after drain so they aren't re-enqueued within a reasonable window (e.g., 20 minutes).
Environment
- OpenClaw version: 2026.2.26
- Channel: Telegram (DM)
- Agent model: Claude Opus 4.6
- Scenario: agent busy with background
exec sessions for 10+ minutes
Bug
When an inbound message arrives while the agent is busy (long-running tool calls), the followup queue can deliver the same message to the agent multiple times across successive drain cycles.
Steps to reproduce
[Queued messages while agent was busy])[Queued messages]blocksExpected behavior
Each inbound message should be delivered to the agent exactly once, regardless of how many drain cycles occur while the agent is busy.
Root cause (analysis)
The inbound dedupe layer (
shouldSkipDuplicateInboundinsrc/auto-reply/reply/inbound-dedupe.ts) correctly prevents duplicate inbound processing using a TTL cache keyed onMessageSid.However, the followup queue dedupe (
isRunAlreadyQueuedinsrc/auto-reply/reply/queue/enqueue.ts) only checks against items currently inqueue.items. When the queue drains viabuildCollectPrompt, delivered items are spliced out (queue.items.splice(0, items.length)). If a subsequent drain cycle re-enqueues the same message, the dedupe check passes because the array no longer contains the previously delivered item.Suggested fix
Apply a TTL-based dedupe cache (similar to
inboundDedupeCache) to the followup queue drain. Track delivered message IDs after drain so they aren't re-enqueued within a reasonable window (e.g., 20 minutes).Environment
execsessions for 10+ minutes