MemberJunction / MemberJunction/MJ

Communication Engine: batch-send error semantics — log failures abort/discard sends, EndRun never marks runs Failed

Open
#2,819 0 comments 0 reactions 1 assignee Claimed by @SDesai-BC View on GitHub
bug
Dominant language
TSQL
Stars
29
Forks
6
Avg merge
1d 8h
Merged PRs (30d)
308

Description

## What's wrong

Several error paths in the Communication Engine's send pipeline (`packages/Communication/engine/src/Engine.ts` + `BaseEngine.ts`) lose or block work in ways that bite any multi-recipient consumer:

1. **A DB hiccup blocks the send entirely**: `SendSingleMessage` throws when `StartLog` fails (`Failed to start log for message`) — the message is never dispatched because an *audit* write failed.
2. **A successful send gets reported as a throw**: after the provider accepts the message, a failure updating the log row throws — the caller sees an exception for an email that was actually delivered (double-send bait on retry).
3. **Template-process and log failures abort the whole batch**: inside `SendMessages`, these throw out of the loop, stranding the remaining recipients unsent and the Communication Run in `Pending`. (Provider-level send failures DO continue correctly — `Success=false` results are pushed and the loop proceeds — so the batch contract is inconsistent across failure classes.)
4. **`EndRun` unconditionally sets `Status='Complete'`** — the Runs schema has `Status='Failed'` and `ErrorMessage` columns, but no engine code path ever populates them.
5. **An `EndRun` failure throws *after* all sends succeeded**, discarding the entire results array.

## Proposed fix

Convert per-message throw sites (1–3) into `Success=false` (or result-coded, see the MessageResult issue) entries in the results array so the batch always continues and always returns per-row outcomes; always reach `EndRun` (try/finally); populate `Failed`/`ErrorMessage` on the run when any row failed or the run aborted; never let audit-write failures mask a successful dispatch (log the audit failure on the result instead).

Found while evaluating the engine for CDP's voice-invite batch emails — these semantics (one bad row must not strand N-1 recipients; an accepted send must never surface as a failure) were the deciding reason CDP kept a hand-rolled send loop.

## Acceptance criteria

- [ ] A `StartLog` failure does not block dispatch; it is recorded on that message's result
- [ ] A post-send log-update failure never converts an accepted send into a thrown error
- [ ] A template-render failure for one recipient fails only that row; the batch continues and the run is ended
- [ ] Runs are marked `Failed` + `ErrorMessage` when appropriate; no run is left `Pending` by an engine error path
- [ ] `EndRun` failure does not discard the results array
- [ ] Tests cover each failure class in a multi-recipient batch

## Blocked by

None - can start immediately (per-row reporting is richer with MemberJunction/MJ#2818)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.