Duplicate messages deduplicated via client-generated ID before rendering
Why it matters
Real-time transports retry delivery on network errors. Without deduplication, a message sent before a disconnect and resent after reconnect renders twice in the UI — the user sees duplicate chat lines, duplicate notifications, and potentially double-processed side effects. CWE-694 (Use of Multiple Resources with Duplicate Identifier) captures this integrity failure. The fix is straightforward but frequently omitted in AI-generated WebSocket code that copies the happy path without handling retry semantics.
Severity rationale
High because network retries cause duplicate message renders and side effects without client-side deduplication, directly corrupting the conversation view.
Remediation
Assign a client-generated ID to each outbound message and maintain a Set of received IDs on the client. Drop any message whose ID has already been processed.
// Receiving
const seen = new Set<string>();
socket.on('message', (msg: { id: string; content: string }) => {
if (seen.has(msg.id)) return;
seen.add(msg.id);
renderMessage(msg);
});
// Sending
const send = (content: string) => {
const id = nanoid(); // generated on the client
socket.emit('send_message', { id, content });
};
Bound the seen Set to a recent window (last N messages or TTL) if memory is a concern — an unbounded Set grows without limit in long-lived sessions.
Detection
-
ID:
client-deduplication -
Severity:
high -
What to look for: Count all client message handlers. For each, classify whether deduplication logic tracks received message IDs. Enumerate the deduplication mechanisms: Set, Map, or database-backed tracking.
-
Pass criteria: The client generates a unique ID for outgoing messages and tracks at least 1 set of received IDs to deduplicate. Duplicate receipts are ignored with 0 duplicate renders.
-
Fail criteria: No deduplication logic, or IDs are not client-generated.
-
Skip (N/A) when: Never — deduplication prevents duplicate renders from network retries.
-
Detail on fail:
"Client message handler has no deduplication. Network retries could cause the same message to be rendered twice." -
Remediation: Implement client-side deduplication:
const receivedMessageIds = new Set<string>(); socket.on('message', (msg: Message) => { if (receivedMessageIds.has(msg.id)) { return; // Ignore duplicate } receivedMessageIds.add(msg.id); renderMessage(msg); });When sending, include a client-generated ID:
const sendMessage = (content: string) => { const clientId = nanoid(); socket.emit('send_message', { clientId, content }); };
External references
- cwe · CWE-694 — Target Hardware Not Supported by Available Code
Taxons
History
- 2026-04-18·v1.0.0·Initial import from community-realtime·automated