From 20d0259433d7e4abb51bae05889a9b2426956eae Mon Sep 17 00:00:00 2001 From: Nuwan Date: Sat, 7 Feb 2026 01:44:23 +0530 Subject: [PATCH] fix(16): fix attachment deduplication in uploadAttachment.fulfilled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix only added deduplication by attachmentId in addMessageFromWebSocket and fetchChatHistory.fulfilled, but missed the uploadAttachment.fulfilled handler. Race condition: WebSocket message can arrive BEFORE the upload API returns. When this happens: 1. WebSocket delivers message with id='456', attachmentId=123 2. addMessageFromWebSocket adds it to state 3. Upload API returns 4. uploadAttachment.fulfilled only checked for id='attachment-123' → not found 5. Duplicate message added Fix: Also check by attachmentId in uploadAttachment.fulfilled handler. Co-Authored-By: Claude Opus 4.5 --- jam-ui/src/store/features/sessionChatSlice.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/jam-ui/src/store/features/sessionChatSlice.js b/jam-ui/src/store/features/sessionChatSlice.js index 2738aa903..a71142e11 100644 --- a/jam-ui/src/store/features/sessionChatSlice.js +++ b/jam-ui/src/store/features/sessionChatSlice.js @@ -468,9 +468,13 @@ const sessionChatSlice = createSlice({ // Use music_notation.id as part of message id to avoid collision with real msg_id const messageId = `attachment-${notation.id}`; - // Check for duplicates (in case WebSocket somehow delivers to sender) - const exists = state.messagesByChannel[channel].some(m => m.id === messageId); - if (!exists) { + // Check for duplicates by both message ID AND attachmentId + // WebSocket may deliver message before this handler runs with different ID format + const existsById = state.messagesByChannel[channel].some(m => m.id === messageId); + const existsByAttachmentId = state.messagesByChannel[channel].some( + m => m.attachmentId && m.attachmentId === notation.id + ); + if (!existsById && !existsByAttachmentId) { const purpose = attachmentType === 'audio' ? 'Audio File' : 'Notation File'; const message = { id: messageId,