[BUG] Chatwoot integration duplicates messages when a device re-delivers the same key.id — getExistingSourceIds exists but is never called on the live path
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- postgresql, typescript
Research direction
Start by reading ChatwootService.createMessage and compare its live path with ChatwootImport.getExistingSourceIds and importHistoryMessages. Trace how source_id and conversationId are passed, then verify that repeated WAID:<key.id> arrivals are ignored while lookup errors still allow delivery to continue. Confirm the existing callers handle the null result as described.
Written by the indexing model from the issue text.
Description
📋 Bug Description
When a sender's device re-delivers the same WhatsApp message (identical key.id, new timestamp — e.g. a buggy device or an unofficial client stuck in a loop resending an auto-reply), the Chatwoot integration creates a new Chatwoot message on every arrival. There is no idempotency check on the live path.
Real-world impact (production, v2.3.7): one guest's device looped an away-message roughly once a minute. In ~25h we accumulated 837 copies of a single message in one Chatwoot conversation (and a second looping key.id later reached 539 copies). The conversation became unusable for the support team. WhatsApp mobile shows the message once (native dedup by key.id); Chatwoot shows every copy.
The interesting part: the codebase already contains exactly the right check — ChatwootImport.getExistingSourceIds(sourceIds, conversationId) queries Chatwoot's messages.source_id for WAID:<key.id> — but it is only called from the history import path (importHistoryMessages). The live path (ChatwootService.createMessage) posts to Chatwoot with source_id: WAID:<key.id> without consulting anything.
🔄 Steps to Reproduce
- Evolution API v2.3.7 (baileys channel) + Chatwoot integration enabled.
- From a sender device, deliver the same message twice with the same
key.id(a looping unofficial client does this naturally; the events arrive withstatus: DELIVERY_ACK,source: 'unknown'and a freshmessageTimestampeach time). - Watch the Chatwoot conversation.
✅ Expected Behavior
Second and subsequent arrivals of an already-imported key.id for the same conversation should be ignored (the same way importHistoryMessages already filters via getExistingSourceIds), or at least be configurable to be ignored.
❌ Actual Behavior
Every arrival creates a new Chatwoot message with the same source_id (WAID:<key.id>). messages.source_id in Chatwoot has a non-unique index, so nothing stops the duplicates downstream either.
💡 Suggested Fix
Call the existing check in the live path. We are running this in production (patched into createMessage) and it fully stopped the flood without affecting legitimate traffic:
// at the top of ChatwootService.createMessage(...), sourceId = "WAID:" + key.id
if (sourceId) {
try {
const existing = await chatwootImport.getExistingSourceIds([sourceId], conversationId);
if (existing.has(sourceId)) {
this.logger.warn(`[dedup] repeated message ignored source_id=${sourceId} conversation_id=${conversationId}`);
return null; // callers already handle the null ("message not sent") path
}
} catch (e) {
this.logger.warn(`[dedup] check unavailable: ${e}`); // fail open
}
}
Notes:
getExistingSourceIdsalready accepts the optionalconversationIdargument, so the lookup is cheap (indexed onsource_id).- Failing open on lookup errors keeps message delivery safe if the Chatwoot DB connection blips.
- Verified in production: 14 duplicate arrivals blocked in the first minutes across 2 looping
key.ids, zero legitimate messages affected.
🌍 Environment
- Evolution API: v2.3.7 (image
evoapicloud/evolution-api:v2.3.7) - Connection type: baileys
- Chatwoot: v4.16.1, integration via
CHATWOOT_ENABLED=true - DB: PostgreSQL (shared instance for Evolution + Chatwoot import connection)
- OS: Linux (Docker)
- Dominant language
- TypeScript
- Stars
- 9.6k
- Forks
- 7.3k
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from evolution-foundation/evolution-api
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 1/5 1-3 hours Newbie friendliness 88/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
evolution-foundation/evolution-api#2700 · 1 comment ·
-
Difficulty 1/5 1-3 hours Newbie friendliness 76/100
All issues in evolution-foundation/evolution-api
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug clawsweeper:linked-pr-open clawsweeper:needs-live-repro clawsweeper:no-new-fix-pr impact:message-loss issue-rating: 🐚 platinum hermit P2 regression
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
danielmiessler/LifeOS#2218 ·