Notifications: 7 pipeline email template slugs missing from seed
Status: ✅ Fixed
Severity: Medium
Component: packages/db/src/seed.ts, apps/servers/notifications/src/templates/email-loader.ts
Symptom
All pipeline milestone emails (strategy ready, deliverable plan ready, activity pipeline dispatched/failed, DM review request, SEO rank alerts, custom reports) silently failed. Notification server logs showed BullMQ email jobs failing with HTTP 400 from SendGrid.
Root Cause
Two compounding issues:
1. Missing seeds. The following templateSlug values were referenced in worker enqueueNotification calls but had no corresponding row in emailTemplate (DB):
| Slug | Worker | Variables |
|---|---|---|
strategy_ready | strategy-writer.worker.ts | firstName, companyName, strategyUrl, currentYear |
deliverable_plan_ready | strategy.worker.ts | firstName, companyName, deliverableCount, goalCount, monthlyCost, reviewUrl, currentYear |
activity_pipeline_dm_review | activity.worker.ts | companyName, activityCount, periodStart, periodEnd, pipelineUrl, currentYear |
activity_pipeline_ready | activity.worker.ts | firstName, companyName, activityCount, enqueuedCount, periodStart, periodEnd, totalCost, pipelineUrl, currentYear |
activity_pipeline_failed | activity.worker.ts | firstName, companyName, periodStart, periodEnd, errorSnippet, planUrl, currentYear |
seo-rank-alert | gsc-keywords-snapshot.job.ts | keyword, change, alertType, currentYear |
report-ready | custom-report-writer.worker.ts | reportLabel, tenantName, requestedBy, currentYear |
2. Silent fallback. email-loader.ts returned { subject: slug, html: "", text: "" } when no row was found instead of throwing. The notification server then called provider.send() with an empty body — SendGrid returns HTTP 400 for emails with no content. The BullMQ job failed but since pipeline workers wrap notification calls in try/catch (non-fatal), the pipeline continued without error logs.
Fix
packages/db/src/seed.ts — Added all 7 missing templates to EMAIL_TEMPLATES. Re-run seed to push to DB:
$env:DATABASE_URL="postgresql://leadmetrics:leadmetrics@localhost:5434/leadmetrics"
npx ts-node packages/db/src/seed.tsapps/servers/notifications/src/templates/email-loader.ts — Changed fallback from silent empty return to a thrown error:
// Before (silent — caused SendGrid 400):
return { subject: slug, html: "", text: "" };
// After (loud — surfaces missing templates immediately):
throw new Error(`Email template "${slug}" not found for tenant ${tenantId} — add it to the seed`);Secondary fix: web handler bug
The web notification handler (web.handler.ts) was passing web_${type} as a literal Handlebars template string instead of looking up the template message from WEB_TEMPLATES. Fixed to use getWebTemplate(type) ?? type. The channel_suggestions_ready type (used by channel-action-suggester, channel: "web") was also added to WEB_TEMPLATES and NotificationType.
Rule going forward
Every enqueueNotification({ channel: "email", templateSlug: "..." }) call must have a corresponding entry in EMAIL_TEMPLATES in packages/db/src/seed.ts. The loader now throws immediately if a slug is missing, so failures surface on first run rather than as silent SendGrid 400s.