Duplicate Email Sent
A user receives two identical email notifications triggered by a single event. This happens either because the notification job retries on a transient failure without checking whether the email was already delivered, or because two separate code paths both trigger an email for the same event — one synchronously and one via a background queue.
MediumIntermediateManual testingAPI testingExploratory testing
// UNDERSTAND
// Symptoms
- User user@example.com receives two identical password reset emails within seconds of requesting a single reset
- Both emails have the same subject, body, and timestamp
- Email delivery logs show two send events for the same recipient and event ID
- Clicking either link works, but each is a separate token — the first token becomes invalid after the second email is sent (if tokens are one-time-use)
- Customer support receives reports of duplicate or confusing repeated emails
// Root Cause
- The notification worker processes events with at-least-once delivery semantics. When a send attempt times out or the ACK is not received, the job retries — but it does not check an idempotency key (e.g. event ID + recipient) before sending, so the email is dispatched a second time even though the first send succeeded at the provider
- Two separate event listeners are registered for the same trigger (user.password_reset_requested): a synchronous email call in the request handler and an async job enqueued to the background queue. Both fire for every event, doubling the send
// Where It Appears
- Password reset and account verification email flows
- Order confirmation emails where the payment webhook retries on timeout
- Background job queues with at-least-once delivery that send transactional emails without deduplication
- Microservice architectures where multiple services subscribe to the same user-event topic
// REPRODUCE & TEST
// How to Reproduce
- 01Log in to a test email account at user@example.com
- 02Navigate to the password reset page at /forgot-password
- 03Enter user@example.com in the email field and click Send Reset Link
- 04Wait 30 seconds for all queued jobs to process
- 05Check the inbox for user@example.com and count the number of password reset emails received
- 06If two or more identical emails arrive, the duplicate-send bug is confirmed
// Test Data Needed
- A test email account at user@example.com that can be monitored in real time (a test inbox such as Mailhog, Mailtrap, or a real email account)
- Access to the /forgot-password endpoint
- Optionally: access to the notification job queue or delivery logs to observe two distinct send events
// Manual Testing Ideas
- Trigger a password reset for user@example.com and check the inbox within 60 seconds — two emails arriving within a few seconds of each other strongly indicate a synchronous-plus-async double-send
- Check the email delivery log or the background job dashboard for two send records with the same recipient and event ID
- Simulate a timeout: in a staging environment, slow the email provider response to trigger a retry — confirm whether a second email is sent
- Test other notification types: order confirmation, welcome email, subscription renewal — check whether duplicates occur on those too
- Trigger the password reset twice in quick succession (two form submissions) and confirm that four emails are not sent — this isolates per-request duplication from per-event duplication
// API Testing Ideas
- POST /api/auth/forgot-password with body { "email": "user@example.com" } and a valid CSRF token or API key
- Assert the response is 200 OK with a message such as 'If this email is registered, you will receive a reset link'
- Wait 30 seconds, then check the test inbox (via Mailtrap API or Mailhog API) for emails sent to user@example.com
- Assert the inbox contains exactly one password reset email — two or more emails confirm the bug
- Check the email delivery log endpoint (if available in staging) for duplicate event IDs with the same recipient
// Automation Idea
In a staging environment connected to Mailtrap or Mailhog, send POST /api/auth/forgot-password for user@example.com. After 30 seconds, query the Mailtrap API or Mailhog API for emails sent to user@example.com with a subject matching 'Password Reset'. Assert the count is exactly 1. If the count is 2 or more, the test fails and reports how many duplicates were delivered.
// Expected Result
A single password reset request sends exactly one email to user@example.com within a reasonable delivery window.
// Actual Result (Example)
After one POST /api/auth/forgot-password request for user@example.com, the test inbox receives two identical password reset emails within 5 seconds of each other. Both emails contain different reset tokens.
// REPORT IT
Example Bug Report
- Title
- Two identical password reset emails delivered to user@example.com from a single reset request
- Severity
- Medium
- Environment
- Staging environment Mailtrap inbox for user@example.com Standard user account
- Steps to Reproduce
- 01Log in to the Mailtrap inbox for user@example.com
- 02Navigate to /forgot-password
- 03Enter user@example.com and click Send Reset Link
- 04Wait 60 seconds
- 05Check the Mailtrap inbox for user@example.com and count the password reset emails received
- Expected Result
- Exactly one password reset email is delivered to user@example.com.
- Actual Result
- Two identical password reset emails arrive in the user@example.com inbox within 5 seconds of each other. Both have the subject 'Reset your password' and different reset tokens.
- Impact
- Users are confused by duplicate emails and may distrust which token to use. Duplicate sends increase email sending costs and risk triggering spam filters. If the first token is invalidated when the second is issued, the first email contains a broken link.