# 02-Jun-2026 — Brevo campaign send reliability, unconditional error logging, and email-composer recipi... [#278](https://github.com/nbarrett/ngx-ramblers/issues/278)

## [build 661](https://github.com/nbarrett/ngx-ramblers/actions/runs/26820402909) — [commit 49f811f](https://github.com/nbarrett/ngx-ramblers/commit/49f811f6e92965622b4096ec218f3941f4ef0101)

_____

> 📖 New to this feature? See [Setting up a Gmail inbox for committee replies](https://ngx-ramblers.org.uk/how-to/technical-articles/2026-05-29-gmail-inbox-setup) for the step-by-step setup guide.

Follow-up to the committee Gmail inbox / mail work. Three related fixes plus a
DRY consolidation of the error-logging convention.

Campaign send:
- Free Brevo plans reject a campaign tag with an empty-body HTTP 500. The send
no longer provokes that failure: createCampaign checks the account plan up
front (cached per API key) and only attaches the tag when the plan supports
it, so free-plan sends succeed on the first call. The 405/500 tag fallback
remains purely as a safety net for when the capability check cannot run.

Error logging (made unconditional and DRY):
- Add server/lib/shared/error-debug-log.ts: createErrorDebugLog(namespace)
returns the standard debug("ERROR:" + envConfig.logNamespace(...)) logger,
memoised and forced enabled, so errors always log.
- Add server/lib/brevo/common/error-log.ts: logBrevoError emits a single
diagnostics object (request method/url + size-capped body with htmlContent
omitted, HttpError statusCode and body, response status message and headers,
error name and message) plus the full stack trace.
- Route the shared Brevo handleError through logBrevoError, and apply it to the
Brevo modules whose catch blocks bypassed handleError.
- Converge every hand-rolled debug("ERROR:" + ...) declaration across the
server (inbox, cloudflare, map-routes, mongo controllers, brevo) onto
createErrorDebugLog; remove dead error loggers that were never used.

Email composer recipient labels:
- Stop the "All Members (4 members)" doubling. listNameImpliesMembers is the
single guard; listNameAndCount and listSubscriberCount both use it. Lists
whose name already ends in member/members render as "All Members - 4";
others as "1 Testing member" / "4 members". The three selector badges now
share listSubscriberCount instead of hand-building the count inline, and the
campaign-sent message drops the redundant "(campaign)" suffix.