Skip to content

fix: stop inbox creating duplicate chats for one contact (#363)#364

Merged
ArnasDon merged 1 commit into
mainfrom
fix/duplicate-conversations
Jul 10, 2026
Merged

fix: stop inbox creating duplicate chats for one contact (#363)#364
ArnasDon merged 1 commit into
mainfrom
fix/duplicate-conversations

Conversation

@ArnasDon

Copy link
Copy Markdown
Owner

Fixes #363.

Problem

Inbound WhatsApp messages fragmented into many duplicate chats for a single number (see the reporter's screenshot).

Root cause: findOrCreateConversation in the webhook used .single() to look up the existing conversation. .single() errors on both 0 rows and ≥2 rows, and the old code treated any error as "none found" and inserted a new row.

  1. First duplicate — a race. Meta retries a delivery, or a batch of messages fans out to concurrent after() runs; two inserts both miss the lookup. Unlike contacts (migration 022), conversations had no UNIQUE index and no unique-violation backstop.
  2. Then it snowballs. Once two conversations existed for a contact, .single() errored on every subsequent inbound message, so the webhook created yet another conversation each time — a wall of duplicate chats.

The public-API resolver (resolve-conversation.ts) had the same latent bug via .maybeSingle() (also errors on ≥2 rows).

Fix

Mirrors the existing contact-dedup pattern (migration 022):

  • supabase/migrations/036_conversation_contact_dedup.sql — merges existing duplicate conversations into the oldest thread (re-points messages, message_reactions, deals, flow_runs, notifications, ai_usage_log; rolls up unread counts + last-message summary — no data loss), then adds UNIQUE (account_id, contact_id). This satisfies the reporter's "old users must be merged into a single chat" ask.
  • Webhook (src/app/api/whatsapp/webhook/route.ts) — lookup now orders oldest-first with .limit(1) (converges on any duplicates instead of erroring), and the insert catches 23505 to re-resolve the winning row.
  • Public-API resolver (src/lib/whatsapp/resolve-conversation.ts) — same hardening.

Important

Migration required. Applying 036 on the production Supabase project is what merges the existing duplicate chats the user is seeing. The code change alone only prevents new duplicates.

Testing

  • tsc --noEmit clean.
  • Added a conversation unique-race test; WhatsApp suites pass (211 tests).

Out of scope

The issue's secondary line — "Chat Media need to downloaded in local" — is a vague feature request, not the reported bug. Media already works via the /api/whatsapp/media/:id proxy + chat_media table (migration 023). Happy to scope a separate change if local/object-storage persistence is wanted.

🤖 Generated with Claude Code

An inbound message could create a second conversation for a contact
under a race (Meta retries a delivery, or a batch fans out to concurrent
after() runs). There was no UNIQUE index and no unique-violation
backstop on conversations, unlike contacts (migration 022). Worse, once
two conversations existed the `.single()` lookup errored on every later
message and the webhook created yet another conversation each time,
snowballing into a wall of duplicate chats.

- findOrCreateConversation now resolves oldest-first with .limit(1)
  instead of .single(), so pre-existing duplicates converge, and catches
  23505 on insert to re-resolve the winning row.
- Same hardening applied to the public-API resolver (resolve-conversation).
- Migration 036 merges existing duplicate conversations into the oldest
  thread (re-pointing messages, reactions, deals, flow_runs,
  notifications, ai_usage_log — no data loss) then adds a
  UNIQUE (account_id, contact_id) index as the authoritative guarantee.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@supabase

supabase Bot commented Jul 10, 2026

Copy link
Copy Markdown

Updates to Preview Branch (fix/duplicate-conversations) ↗︎

Deployments Status Updated
Database Fri, 10 Jul 2026 10:00:10 UTC
Services Fri, 10 Jul 2026 10:00:10 UTC
APIs Fri, 10 Jul 2026 10:00:10 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Fri, 10 Jul 2026 10:00:24 UTC
Migrations Fri, 10 Jul 2026 10:00:34 UTC
Seeding Fri, 10 Jul 2026 10:00:37 UTC
Edge Functions Fri, 10 Jul 2026 10:00:38 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

@ArnasDon ArnasDon merged commit b867760 into main Jul 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Inbox chat created multiple chats for single user/number

1 participant