{"id":"6a1ed4311cad34751ce6872a","title":"2026 06 02 Build 661","path":"how-to/committee/release-notes/2026-06-02-build-661","contentMarkdown":"# 02-Jun-2026 — Brevo campaign send reliability, unconditional error logging, and email-composer recipi... [#278](https://github.com/nbarrett/ngx-ramblers/issues/278)\n\n## [build 661](https://github.com/nbarrett/ngx-ramblers/actions/runs/26820402909) — [commit 49f811f](https://github.com/nbarrett/ngx-ramblers/commit/49f811f6e92965622b4096ec218f3941f4ef0101)\n\n_____\n\n> 📖 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.\n\nFollow-up to the committee Gmail inbox / mail work. Three related fixes plus a\nDRY consolidation of the error-logging convention.\n\nCampaign send:\n- Free Brevo plans reject a campaign tag with an empty-body HTTP 500. The send\nno longer provokes that failure: createCampaign checks the account plan up\nfront (cached per API key) and only attaches the tag when the plan supports\nit, so free-plan sends succeed on the first call. The 405/500 tag fallback\nremains purely as a safety net for when the capability check cannot run.\n\nError logging (made unconditional and DRY):\n- Add server/lib/shared/error-debug-log.ts: createErrorDebugLog(namespace)\nreturns the standard debug(\"ERROR:\" + envConfig.logNamespace(...)) logger,\nmemoised and forced enabled, so errors always log.\n- Add server/lib/brevo/common/error-log.ts: logBrevoError emits a single\ndiagnostics object (request method/url + size-capped body with htmlContent\nomitted, HttpError statusCode and body, response status message and headers,\nerror name and message) plus the full stack trace.\n- Route the shared Brevo handleError through logBrevoError, and apply it to the\nBrevo modules whose catch blocks bypassed handleError.\n- Converge every hand-rolled debug(\"ERROR:\" + ...) declaration across the\nserver (inbox, cloudflare, map-routes, mongo controllers, brevo) onto\ncreateErrorDebugLog; remove dead error loggers that were never used.\n\nEmail composer recipient labels:\n- Stop the \"All Members (4 members)\" doubling. listNameImpliesMembers is the\nsingle guard; listNameAndCount and listSubscriberCount both use it. Lists\nwhose name already ends in member/members render as \"All Members - 4\";\nothers as \"1 Testing member\" / \"4 members\". The three selector badges now\nshare listSubscriberCount instead of hand-building the count inline, and the\ncampaign-sent message drops the redundant \"(campaign)\" suffix.","contentHtml":"<h1>02-Jun-2026 — Brevo campaign send reliability, unconditional error logging, and email-composer recipi... <a href=\"https://github.com/nbarrett/ngx-ramblers/issues/278\">#278</a></h1>\n<h2><a href=\"https://github.com/nbarrett/ngx-ramblers/actions/runs/26820402909\">build 661</a> — <a href=\"https://github.com/nbarrett/ngx-ramblers/commit/49f811f6e92965622b4096ec218f3941f4ef0101\">commit 49f811f</a></h2>\n<hr>\n<blockquote>\n<p>📖 New to this feature? See <a href=\"https://ngx-ramblers.org.uk/how-to/technical-articles/2026-05-29-gmail-inbox-setup\">Setting up a Gmail inbox for committee replies</a> for the step-by-step setup guide.</p>\n</blockquote>\n<p>Follow-up to the committee Gmail inbox / mail work. Three related fixes plus a\nDRY consolidation of the error-logging convention.</p>\n<p>Campaign send:</p>\n<ul>\n<li>Free Brevo plans reject a campaign tag with an empty-body HTTP 500. The send\nno longer provokes that failure: createCampaign checks the account plan up\nfront (cached per API key) and only attaches the tag when the plan supports\nit, so free-plan sends succeed on the first call. The 405/500 tag fallback\nremains purely as a safety net for when the capability check cannot run.</li>\n</ul>\n<p>Error logging (made unconditional and DRY):</p>\n<ul>\n<li>Add server/lib/shared/error-debug-log.ts: createErrorDebugLog(namespace)\nreturns the standard debug(&quot;ERROR:&quot; + envConfig.logNamespace(...)) logger,\nmemoised and forced enabled, so errors always log.</li>\n<li>Add server/lib/brevo/common/error-log.ts: logBrevoError emits a single\ndiagnostics object (request method/url + size-capped body with htmlContent\nomitted, HttpError statusCode and body, response status message and headers,\nerror name and message) plus the full stack trace.</li>\n<li>Route the shared Brevo handleError through logBrevoError, and apply it to the\nBrevo modules whose catch blocks bypassed handleError.</li>\n<li>Converge every hand-rolled debug(&quot;ERROR:&quot; + ...) declaration across the\nserver (inbox, cloudflare, map-routes, mongo controllers, brevo) onto\ncreateErrorDebugLog; remove dead error loggers that were never used.</li>\n</ul>\n<p>Email composer recipient labels:</p>\n<ul>\n<li>Stop the &quot;All Members (4 members)&quot; doubling. listNameImpliesMembers is the\nsingle guard; listNameAndCount and listSubscriberCount both use it. Lists\nwhose name already ends in member/members render as &quot;All Members - 4&quot;;\nothers as &quot;1 Testing member&quot; / &quot;4 members&quot;. The three selector badges now\nshare listSubscriberCount instead of hand-building the count inline, and the\ncampaign-sent message drops the redundant &quot;(campaign)&quot; suffix.</li>\n</ul>\n"}