From f4af7709b0c04cc9238dab6b4f2cde96147d284e Mon Sep 17 00:00:00 2001 From: Maciej Pienczyn Date: Sat, 11 Apr 2026 09:58:47 +0200 Subject: [PATCH] fix: prevent duplicate messages from SSE/poll race condition Deduplicate by sender+content match regardless of _optimistic flag, preventing double display when SSE event arrives before POST response. Co-Authored-By: Claude Opus 4.6 (1M context) --- static/js/conversations.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/static/js/conversations.js b/static/js/conversations.js index 5b539cd..445a114 100644 --- a/static/js/conversations.js +++ b/static/js/conversations.js @@ -775,17 +775,19 @@ var convId = msg.conversation_id; if (!state.messages[convId]) state.messages[convId] = []; - // Dedup: skip if message with this ID already exists - // Dedup: by real ID or by matching optimistic message (same sender + content) + // Dedup: skip if message already exists by ID, content match, or time proximity var dominated = state.messages[convId].some(function(m) { if (m.id === msg.id) return true; - // Match optimistic msg: same sender, same stripped content - if (m._optimistic && msg.sender_id && m.sender_id === msg.sender_id) { + // Match by same sender + same stripped content (optimistic or SSE/poll race) + if (msg.sender_id && m.sender_id === msg.sender_id) { var mc = (m.content || '').replace(/<[^>]*>/g, '').trim(); var nc = (msg.content || '').replace(/<[^>]*>/g, '').trim(); if (mc && nc && mc === nc) { - m.id = msg.id; // Update to real ID - m._optimistic = false; + // Update to real ID if this was optimistic + if (m._optimistic) { + m.id = msg.id; + m._optimistic = false; + } return true; } }