Common Chat Functionality Bugs and How to Catch Them
Common Chat Functionality Bugs and How to Catch Them
Common Chat Functionality Bugs and How to Catch Them
Overview of Chat Functionality Bugs
Chat features sit at the heart of many modern applications, from customer‑support portals to social networks and internal collaboration tools. Because they combine real‑time networking, stateful UI components, media handling, and accessibility concerns, they expose a wide variety of defects that often slip through scripted test suites. This guide walks through the most frequent chat‑specific bug patterns, explains why each arises, shows how users experience the problem, and provides reproducible steps, detection tactics, and remediation advice.
The goal is to give you a practical test matrix you can copy into your test plan, a set of manual checks you can run today, and automation snippets that integrate with common frameworks (Appium, Playwright, Selenium). By the end you will have a concrete checklist and a deeper understanding of how persona‑driven autonomous exploration—such as the approach used by SUSATest—can surface issues that deterministic scripts miss.
---
Bug Pattern 1: Message Loss Under High Load
Why it happens
When a chat server receives a burst of messages—often triggered by a notification storm, a bot flood, or a sudden spike in active users—its inbound queue can overflow if the consumer side (the process that writes to persistent storage) cannot keep up. Many implementations use a simple in‑memory buffer with a fixed size; once the buffer fills, incoming payloads are dropped without acknowledgment to the sender.
User impact
Users see their sent messages disappear from the conversation view, while the remote party never receives them. In group chats, the loss may appear to the loss can be asymmetric, leading to confusion about who said what.
How to reproduce
- Open two client instances (A and B) logged into the same chat room.
- From a third scripted client (C) send a rapid burst of 500‑1000 text messages in under 5 seconds using a loop that calls the send API without waiting for acknowledgments.
- Monitor the message count stored in the database for the room and compare it to the number sent from C.
- Observe that the count plateaus well below the number of sent messages.
A reproducible Bash snippet using curl (assuming a REST endpoint /api/messages) looks like:
#!/usr/bin/env bash
ROOM_ID="room123"
TOKEN="Bearer <your‑token>"
for i in {1..800}; do
curl -s -X POST "https://chat.example.com/api/messages" \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"room_id\":\"$ROOM_ID\",\"body\":\"load‑test $i\"}" &
done
wait
Detection strategies
- Load‑testing tools – JMeter, k6, or Gatling can sustain a configurable message‑per‑second rate and assert that the server returns 200 OK for each request.
- Server‑side metrics – Watch the inbound queue length and the consumer lag (e.g., via Prometheus). A rising lag that never recovers signals a bottleneck.
- Client‑side validation – After sending a batch, have the client request the last N messages and verify that every sent ID appears.
Fix and prevention
- Replace fixed‑size in‑memory buffers with a back‑pressure‑aware queue (e.g., LinkedBlockingQueue with a dynamic size or a reactive stream).
- Implement explicit acknowledgment (ACK/NACK) from the persistence layer so the sender can retry on failure.
- Add rate‑limiting per connection and global burst limits, returning 429 Too Many Requests when the system is near capacity.
- In automated tests, include a sustained‑load scenario that runs for at least 30 seconds and asserts zero message loss.
---
Bug Pattern 2: Duplicate Message Delivery
Why it happens
Duplicates commonly arise when the client’s retry logic does not deduplicate based on a stable message identifier. If a network glitch causes the original POST to timeout, the client may resend the same payload; the server, lacking a unique constraint on (room_id, client_message_id), stores a second copy.
User impact
The conversation view shows the same text bubble twice in a row, which can be misleading—especially when the duplicate contains a timestamp that appears to go backwards. In moderation contexts, duplicated spam or abusive content may trigger false‑positive alerts.
How to reproduce
- Enable artificial latency on the network interface (e.g., using
tcon Linux) to add 200 ms delay and occasional packet loss. - Send a single message from client A while the latency is active.
- Observe that the client times out after its default timeout (often 5 s) and automatically retries.
- Check the server logs or database: two rows with identical
client_message_idexist.
A minimal tc command to add delay and loss:
sudo tc qdisc add dev eth0 root netem delay 200ms loss 10%
After the test, remove the rule:
sudo tc qdisc del dev eth0 root netem
Detection strategies
- Idempotency checks – After each send, query the message list and ensure the count of messages with a given
client_message_idis exactly one. - Log‑based duplication detection – Index the
client_message_idfield and set a unique index; any insert violation will surface as a duplicate‑key error in the DB logs. - Client‑side deduplication – Maintain a short‑lived cache (e.g., a Set) of IDs sent in the last N seconds and skip UI rendering if the ID is already present.
Fix and prevention
- Enforce a unique constraint on
(room_id, client_message_id)at the data store level. - Make the send API idempotent: if a message with the supplied ID already exists, return the existing record instead of creating a new one.
- On the client, wait for an explicit ACK before clearing the outgoing buffer; if a timeout occurs, retry only after confirming the ID is not already persisted (e.g., via a lightweight GET).
- Include a duplicate‑message scenario in your regression suite: send a message, induce a timeout, send again, assert single storage entry.
---
Bug Pattern 3: Incorrect Timestamp Handling
Why it happens
Chat applications often rely on device clocks to display timestamps, then send those values to the server for persistence. When clients operate in different time zones or have incorrect system time, the server may store the raw client‑supplied timestamp, leading to ordering issues when messages are later sorted by that field. Some backends also convert timestamps incorrectly between UTC and local zones during retrieval.
User impact
Messages appear out of chronological order, causing confusion in fast‑moving threads. In support tickets, a reply may seem to precede the original inquiry, making it difficult for agents to follow the conversation.
How to reproduce
- Set the system clock on client A to be 5 minutes ahead of UTC.
- Send a message from client A at 12:00 UTC (according to a reliable external source).
- Send a second message from client B (clock correct) at 12:01 UTC.
- Retrieve the message list via the API and observe that the message from A appears after B’s, despite being sent earlier in real time.
A quick way to shift the clock on macOS for testing:
sudo date -u 12050000 # sets date to Dec 5 00:00:00 UTC
Remember to revert after testing.
Detection strategies
- Server‑side timestamp enforcement – Upon receipt, override the client‑provided timestamp with the server’s own UTC time (
ServerTimestamp = now()). Store only that value. - Client‑side sanity check – Compare the received timestamp with the device’s monotonic clock (e.g.,
SystemClock.elapsedRealtime()on Android) and discard outliers beyond a reasonable threshold (e.g., ±2 minutes). - API contract test – Send a message with a deliberately incorrect timestamp (e.g., year 2025) and assert that the stored timestamp falls within a few seconds of the request receipt time.
Fix and prevention
- Never trust client‑supplied wall‑clock time for ordering; always replace it with a server‑generated UTC timestamp at ingest.
- If you must display a local time, convert the stored UTC value to the user’s zone on the client using a trusted library (e.g.,
java.time,date‑fns). - Add unit tests that simulate clock skew and verify that message order remains correct.
- Include a test case where you deliberately send a message with a future timestamp and confirm the server discards or corrects it.
---
Bug Pattern 4: Message Ordering Issues
Why it happens
Even with correct timestamps, ordering can break when the client processes incoming messages from multiple sources (WebSocket, long‑poll, REST fallback) and merges them into a single list without respecting sequence numbers. If the transport does not guarantee delivery order (e.g., UDP‑based signaling or a message broker that allows out‑of‑order delivery), the UI may show older messages after newer ones.
User impact
Conversations become hard to follow; users may respond to outdated information, leading to repetitive exchanges or missed context. In threaded discussions, replies may appear under the wrong parent message.
How to reproduce
- Configure the chat server to add a random delay (0‑500 ms) to each outgoing WebSocket frame for a specific user (use a proxy like
toxiproxy). - Have three clients (A, B, C) send messages in rapid succession: A→B→C.
- Observe the UI on a fourth client (D) that subscribes to the room; the messages may appear as C, A, B or another permutation.
Example toxiproxy command to add jitter:
toxiproxy-cli create chat-proxy -l localhost:8080 -u upstream:8080
toxiproxy-cli toxic add chat-proxy --type latency --attribute latency=200ms --attribute jitter=300ms
Detection strategies
- Sequence numbers – Assign a monotonically increasing
msg_seqper sender and have the client buffer out‑of‑order messages until the next expected seq arrives. - UI ordering test – After a burst of messages, collect the rendered message IDs from the DOM and assert they match the expected order based on seq numbers.
- Network simulation – Use tools like
tc netemortoxiproxyto inject delay jitter and loss, then run your automated chat scenario.
Fix and prevention
- Implement a per‑sender sequence number in the message payload; the client holds a sliding window and only renders when the next expected seq is available.
- Prefer transports that guarantee order (WebSocket over TCP, HTTP/2) for the primary chat channel; treat unordered channels as secondary (e.g., UDP for voice) as separate streams.
- Provide a “reorder buffer” UI component that shows a placeholder (“…”) while waiting for missing seq numbers, preventing jumps in the scroll position.
- In your test matrix, add a scenario with intentional jitter and verify that the final rendered list respects sequence numbers.
---
Bug Pattern 5: Failed Message Retry Logic
Why it happens
Clients often implement exponential back‑off retries for failed sends, but they may neglect to clear the retry timer when the user manually deletes the message or navigates away from the chat view. Consequently, a stale retry attempt can fire after the message has been removed, causing a phantom send or an error dialog that confuses the user.
User impact
Users see an error toast (“Failed to send message”) for a message they already deleted, or they notice a duplicate send attempt that appears out of nowhere. In some cases, the retry can resend a message containing sensitive data after the user thought it was discarded.
How to reproduce
- In the chat UI, type a message and hit send.
- Immediately after tapping send, swipe left to delete the message from the composer before the network call completes.
- Disable network connectivity (e.g., toggle airplane mode) to force the send to fail.
- Wait for the client’s retry interval (often 5‑10 seconds) and then re‑enable network.
- Observe whether a retry occurs despite the message being deleted.
A script to toggle network on Android via ADB:
# disable wifi
adb shell svc wifi disable
# wait 8 seconds
sleep 8
# re-enable wifi
adb shell svc wifi enable
Detection strategies
- State‑tracking unit test – Mock the networking layer and verify that a delete action cancels any pending retry timers.
- Integration test with network flakiness – Use a tool like
Network Link Conditioner(macOS) orClumsy(Windows) to drop packets after send, then trigger a UI delete and confirm no retry occurs. - Logging – Ensure the client logs a “retry cancelled” event when the message is removed from the pending set.
Fix and prevention
- Tie each outgoing message to a unique identifier stored in a
PendingMessagemap keyed by the composer instance. When the composer is cleared or the message is removed, delete the entry and cancel its associated timer. - Use a debounced “send request” function that checks the current composer state before actually performing the network call.
- In automated tests, include a step that deletes the message during the back‑off window and asserts zero retry attempts.
---
Bug Pattern 6: Presence Indicator Glitches
Why it happens
Presence (online/away/typing) is usually conveyed via lightweight packets (e.g., WebSocket ping/pong or custom presence events). If the client fails to send a presence-offline when the app goes to the background or the process is killed, the server may continue to show the user as online. Similarly, typing indicators can linger if the typing-stop event is not dispatched after a timeout or when the user navigates away.
User impact
Contacts see stale presence, leading to misguided expectations (e.g., expecting an immediate reply when the user is actually offline). Persistent typing indicators can cause anxiety or make users think the other party is stuck composing a message.
How to reproduce
- Have two clients (A and B) logged in and showing each other as online.
- Put client A into the background (press home button) or kill its process via task manager.
- Observe the presence indicator on client B: does it change to “offline/away” within the expected timeout (usually 30‑60 seconds)?
- For typing, start typing in A’s composer, then navigate to another screen without sending; check whether B’s typing indicator disappears after the configured idle period (often 3 seconds).
Detection strategies
- Background/lifecycle hooks – Verify that
onPause,onStop, oronDestroy(Android) /viewDidDisappear(iOS) trigger a presence‑offline publish. - Event‑audit – Subscribe to the presence stream and assert that every
onlineevent is eventually followed by anofflineevent within a bounded window after the client backgrounds. - Automated UI test – Use Espresso/XCUITest to background the app and then query the remote client’s presence label via accessibility IDs.
Fix and prevention
- Bind presence updates to the app’s lifecycle: publish
onlineononResume/viewDidAppearandofflineononPause/viewDidDisappear. - Implement a server‑side “last seen” timestamp that expires after a configurable interval (e.g., 2 minutes) if no heartbeat is received.
- For typing, start a timer on each keypress; reset it on each new input; send
typing-stopwhen the timer expires or when the view loses focus. - Include a test scenario where the app is backgrounded mid‑typing and confirm that both presence and typing states revert correctly.
---
Bug Pattern 7: Media Upload/Download Failures
Why it happens
Chat platforms frequently allow users to share images, videos, or files. Uploads typically go through a multipart POST to a storage service (e.g., S3, Azure Blob). Failures can stem from:
- Incorrect
Content-Typeheaders causing the storage service to reject the payload. - Network interruptions mid‑stream that are not resumed.
- Client‑side file‑size limits that are not enforced before attempting upload, leading to 413 errors that are not surfaced to the user.
- Download URLs that expire or require authentication tokens the client fails to refresh.
User impact
Users see a stuck progress bar, a generic “Failed to send” toast, or a broken image placeholder. In group chats, a missing media file can break the flow of a conversation (e.g., a product image in a sales chat).
How to reproduce
- Upload failure due to content type – Pick a PNG file, rename its extension to
.txt, and attempt to send it as an image. The server may reject it with 415 Unsupported Media Type. - Upload interruption – Begin uploading a large video ( > 50 MB ), then enable airplane mode halfway through. After restoring connectivity, check whether the client retries from the beginning or resumes.
- Download token expiry – Retrieve a media URL, wait longer than the token’s TTL (e.g., 10 minutes), then try to fetch the image; expect a 403 or 401.
A curl example to simulate a bad content type:
curl -X POST https://chat.example.com/api/media \
-H "Authorization: Bearer <token>" \
-F "file=@/tmp/fake.txt;type=image/png" \
-F "room_id=room123"
Detection strategies
- Contract tests – Validate that the upload endpoint returns appropriate error codes for unsupported MIME types, oversized payloads, and missing authentication.
- Retry/resume verification – After an artificial network drop, confirm that the client either retries with exponential back‑off or attempts a range‑request resumable upload (if supported).
- Media integrity check – After download, compute a hash (SHA‑256) of the file and compare it to the hash provided by the server (if any).
- Accessibility – Ensure that error messages are announced by screen readers and that retry buttons are focusable.
Fix and prevention
- Perform client‑side MIME validation using the file’s magic numbers, not just extension, before constructing the multipart request.
- Enforce file‑size limits in the UI (gray out the send button) and show a clear warning if the user attempts to exceed them.
- Use resumable upload protocols (Tus, S3 multipart) that can continue after a interruption.
- Serve media via short‑lived, signed URLs that include a refresh mechanism; the client should automatically request a new URL when a 403/401 is received.
- In your test matrix, include a media‑upload‑under‑flaky‑network case and a media‑download‑after‑token‑expiry case.
---
Bug Pattern 8: Accessibility Violations in Chat UI
Why it happens
Chat interfaces often rely heavily on custom components (message bubbles, avatars, reaction bars) built with Users who depend on screen readers or keyboard navigation may be unable to send messages, hear new arrivals, or interact with reactions. This can lead to exclusion, especially in accessibility‑regulated sectors (government, education, healthcare). Example axe CLI command: --- Chat messages often accept rich content (markdown, HTML, emojis). If the application renders user‑supplied markup without proper sanitization, an attacker can inject scripts that execute in the context of other users (stored XSS). Similarly, insufficient validation of URLs in link previews can lead to SSRF or malicious redirects. Compromised accounts, session hijacking, defacement of the chat interface, or exfiltration of data. In enterprise environments, a single XSS payload can spread to many users quickly. A quick test using --- Chat apps often store and transmit messages as UTF‑8, but certain code paths (e.g., legacy databases, file‑based logs, or third‑party webhooks) may default to ISO‑8859‑1 or Windows‑1252. When users send characters outside the Latin‑1 range (emoji, CJK glyphs, accented letters), those bytes can be mangled, resulting in garbled text or replacement characters (). Messages appear as nonsense symbols, breaking communication. In multilingual support teams, this can cause misunderstandings and escalate frustration. A quick Python snippet to send UTF‑8 data via --- The following table summarizes which verification techniques work best for each bug pattern. “✓” indicates a strong fit, “△” a partial or situational fit, and “✗” a poor fit. --- Scripted tests excel at checking known conditions, but they often follow a fixed path and cannot adapt to the myriad ways real users interact with a chat interface. Autonomous agents that simulate distinct user personas—curious, impatient, novice, adversarial, elderly, Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.aria-label on action buttons, insufficient color contrast for text inside bubbles, lack of keyboard‑navigable focus order, and missing live region announcements for incoming messages.
User impact
How to reproduce
npx axe-cli https://chat.example.com/rooms/room123 > axe-report.json
Detection strategies
aria-live="polite" region.Fix and prevention
for send, for composer, with aria-live for the message list.aria-label to icon‑only buttons (e.g., “Send”, “Add attachment”, “Emoji”).Bug Pattern 9: Security Flaws (e.g., XSS in chat)
Why it happens
User impact
How to reproduce
(or an SVG with an onload handler).http://169.254.169.254/latest/meta-data/ (AWS metadata) and check whether the server makes an outbound request to that address (monitor via proxy logs).curl to post a malicious payload:
curl -X POST https://chat.example.com/api/messages \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"room_id":"room123","body":"<script>alert(1)</script>"}'
Detection strategies
innerHTML usage.script-src 'self').Fix and prevention
unsafe-inline and eval, and report violations to a monitoring endpoint. or on* attributes.Bug Pattern 10: Localization and Encoding Problems
Why it happens
User impact
How to reproduce
🙋♀️ こんにちは café 🇺🇸.requests:
import requests
url = "https://chat.example.com/api/messages"
headers = {"Authorization": "Bearer <token>", "Content-Type": "application/json"}
payload = {"room_id":"room123","body":"🙋♀️ こんにちは café 🇺🇸"}
r = requests.post(url, json=payload, headers=headers)
print(r.text)
Detection strategies
utf8mb4 (MySQL) or UTF8 (PostgreSQL) and that the connection string specifies the correct charset.? or \x.Fix and prevention
str, Java String, Kotlin String). Avoid byte‑level manipulations unless you explicitly manage encoding.Test Matrix: Manual vs Automated Approaches
Bug Pattern Manual Exploratory Testing Automated Unit / Integration Test Load / Stress Test Accessibility Scan Security Scan Message loss under high load △ (observe missing msgs) ✓ (send batch, assert count) ✓ (sustained rate) ✗ ✗ Duplicate message delivery △ (spot duplicate bubbles) ✓ (unique‑ID DB constraint) △ (retry storms) ✗ ✗ Incorrect timestamp handling △ (notice order oddities) ✓ (server‑override timestamp) △ (time‑skew sim) ✗ ✗ Message ordering issues ✓ (jitter + UI check) ✓ (sequence‑number buffer) ✓ (network emulation) ✗ ✗ Failed message retry logic ✓ (delete during back‑off) ✓ (timer cancel on delete) △ (network flap) ✗ ✗ Presence indicator glitches ✓ (background/app kill) ✓ (lifecycle hooks) △ (heartbeat loss) ✗ ✗ Media upload/download failures ✓ (fail mid‑upload, token expiry) ✓ (contract + retry) ✓ (large file concurrency) ✗ ✗ Accessibility violations in chat UI ✓ (keyboard, screen‑reader) ✓ (axe/pa11y CI) ✗ ✓ (WCAG) ✗ Security flaws (XSS/SSRF) △ (manual payload) ✓ (sanitization unit test) △ (rate‑limit abuse) ✗ ✓ (ZAP/Burp) Localization and encoding problems ✓ (visual garbled text) ✓ (UTF‑8 round‑trip test) ✗ ✗ ✗ How to use the matrix
Persona‑Driven Autonomous Exploration: Why It Finds What Scripts Miss
Test Your App Autonomously