Skip to content

fix: surface failed campaign recipients#415

Merged
KMKoushik merged 4 commits into
mainfrom
codex/campaign-failure-events
Jul 10, 2026
Merged

fix: surface failed campaign recipients#415
KMKoushik merged 4 commits into
mainfrom
codex/campaign-failure-events

Conversation

@KMKoushik

@KMKoushik KMKoushik commented Jul 10, 2026

Copy link
Copy Markdown
Member

Closes #408

Builds on #411 by @tejassinghbhati. Their original skip-and-continue commit is preserved in this branch so its authorship remains in the Git history.

What changed

  • Continue processing the campaign when one contact fails.
  • Persist a FAILED email and EmailEvent containing the failure message for every skipped contact.
  • Reuse an email that was created before campaign linking or queueing failed.
  • Let failure persistence errors escape, so the campaign cursor cannot advance when the failure cannot be recorded.
  • Await BullMQ queue.add() so asynchronous enqueue failures reach the campaign failure handler.
  • Stop swallowing CampaignEmail persistence failures.

User impact

Failed campaign recipients now appear with a FAILED status in campaign activity and the email list. Opening the email shows the underlying message in the existing Events History UI. Other contacts in the batch continue processing.

Verification

  • pnpm --filter web exec vitest run -c vitest.unit.config.ts src/server/service/campaign-service.unit.test.ts
  • Targeted ESLint on the four changed files with zero warnings
  • pnpm --filter web exec tsc --noEmit

Summary by cubic

Keep campaign batches moving when individual contacts fail. Each failed contact is recorded as a FAILED email with an event and error message; enqueue errors from BullMQ are surfaced.

  • Bug Fixes
    • Skip failing contacts; batch continues, even if recording the failure fails.
    • Record failures in a transaction: reuse or create the email, link it to the campaign/contact, set FAILED, and add an event with the error message.
    • Stop swallowing CampaignEmail linking errors; let them reach the batch handler to be recorded and continue.
    • Await BullMQ queue.add() so enqueue errors are captured instead of being lost.

Written for commit 06bca69. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Campaign batches now continue processing remaining contacts when an individual email fails.
    • Failed email attempts are consistently recorded with failure status and error details.
    • Email queueing now waits for confirmation that the job was added successfully.
  • Tests

    • Added coverage for recording failures across existing and new campaign email records.

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
unsend-marketing Ready Ready Preview, Comment Jul 10, 2026 8:29pm

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds transactional recording for campaign contact email failures, including failed email status, campaign links, and email events. Campaign processing now propagates campaign-email creation errors to per-contact handling, logs failures, records them, and continues processing subsequent contacts. Email queue job creation is now awaited. Unit tests cover existing, new, and reused email records.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address #408 by catching per-contact failures, logging them, and continuing batch processing so the campaign can advance.
Out of Scope Changes check ✅ Passed The added tests and queue await are directly tied to the campaign-failure handling fix and do not appear unrelated.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: surfacing failed campaign recipients and persisting their failures.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 10, 2026

Copy link
Copy Markdown

Deploying usesend with  Cloudflare Pages  Cloudflare Pages

Latest commit: 06bca69
Status: ✅  Deploy successful!
Preview URL: https://692be94a.usesend.pages.dev
Branch Preview URL: https://codex-campaign-failure-event.usesend.pages.dev

View logs

@KMKoushik
KMKoushik marked this pull request as ready for review July 10, 2026 20:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/server/service/campaign-contact-failure-service.ts`:
- Line 2: Update the db import in the campaign contact failure service to use
the configured `~/` alias, replacing the relative `../db` path with
`~/server/db`.

In `@apps/web/src/server/service/campaign-service.ts`:
- Around line 1044-1061: recordCampaignContactFailure can throw from the
contact-processing catch block and abort batch iteration. Wrap its invocation
within a nested try/catch, log failures with the contact and campaign
identifiers, and allow execution to continue so the cursor advancement after the
catch still occurs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 79ae3fd1-59a7-4eed-8a02-b1bbfe905c28

📥 Commits

Reviewing files that changed from the base of the PR and between d282d85 and 66a470c.

📒 Files selected for processing (4)
  • apps/web/src/server/service/campaign-contact-failure-service.ts
  • apps/web/src/server/service/campaign-contact-failure-service.unit.test.ts
  • apps/web/src/server/service/campaign-service.ts
  • apps/web/src/server/service/email-queue-service.ts

@@ -0,0 +1,103 @@
import { Campaign, Contact } from "@prisma/client";
import { db } from "../db";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use ~/ alias for the db import.

The coding guidelines for apps/web/src/**/*.{ts,tsx} require the ~/ alias for imports from src. Use ~/server/db instead of the relative ../db.

🔧 Proposed fix
-import { db } from "../db";
+import { db } from "~/server/db";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { db } from "../db";
import { db } from "~/server/db";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/server/service/campaign-contact-failure-service.ts` at line 2,
Update the db import in the campaign contact failure service to use the
configured `~/` alias, replacing the relative `../db` path with `~/server/db`.

Source: Coding guidelines

Comment thread apps/web/src/server/service/campaign-service.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/web/src/server/service/campaign-service.unit.test.ts (1)

103-107: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Consider adding coverage for the batch continue-on-failure path.

These tests exercise recordCampaignContactFailure persistence in isolation, but the central behavior of this PR — a per-contact failure being caught, recorded, and the loop continuing to subsequent contacts (and the cursor still advancing) — isn't covered. A worker-level test asserting that one throwing processContactEmail doesn't abort the batch would guard against regressing issue #408.

As per path instructions, add tests when changes impact logic or behavior in apps/web.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/server/service/campaign-service.unit.test.ts` around lines 103 -
107, Add a worker-level test covering the batch continue-on-failure behavior:
mock one contact’s processContactEmail call to throw, assert
recordCampaignContactFailure is invoked, verify later contacts are still
processed, and confirm the cursor advances. Place it alongside the existing
recordCampaignContactFailure tests and follow the repository’s app/web test
conventions.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/web/src/server/service/campaign-service.unit.test.ts`:
- Around line 103-107: Add a worker-level test covering the batch
continue-on-failure behavior: mock one contact’s processContactEmail call to
throw, assert recordCampaignContactFailure is invoked, verify later contacts are
still processed, and confirm the cursor advances. Place it alongside the
existing recordCampaignContactFailure tests and follow the repository’s app/web
test conventions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 08ec1da1-6c4e-45ac-acfe-f742d24a04a9

📥 Commits

Reviewing files that changed from the base of the PR and between 66a470c and 06bca69.

📒 Files selected for processing (2)
  • apps/web/src/server/service/campaign-service.ts
  • apps/web/src/server/service/campaign-service.unit.test.ts

@KMKoushik
KMKoushik merged commit 5259c7b into main Jul 10, 2026
4 checks passed
@KMKoushik
KMKoushik deleted the codex/campaign-failure-events branch July 10, 2026 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐞 - Campaign halts after first email — one failing contact wedges the whole batch permanently

2 participants