fix(calendar-reminders): null BasicProperties.Headers NREs the reminder publish when tracing is inactive (CS8602) #670

Closed
opened 2026-07-17 17:24:32 +00:00 by spikerj · 2 comments
Owner

Real latent NRE found via CS8602 triage in the calendar-reminder publish path.

CalendarReminderHostedService.PublishNotificationEvent builds BasicProperties without Headers (so Headers is null), then relies on ActivityHelper.InjectTraceContext(properties, publishActivity) to allocate Headers before writing the business-context headers (event.id, user.id).

But InjectTraceContext returns early without allocating Headers when there is no ambient activity (activity ??= Activity.Current; if (activity == null) return;). That is exactly the case on a timer/consumer-driven publish where:

  • _activitySource.StartActivity(...) returns null because the ActivitySource has no registered listener (the failure mode MessagePublishingSourceIsRegisteredTests guards against), and
  • Activity.Current is also null (no ambient request trace).

Headers then stays null and properties.Headers["event.id"] = ... throws an NRE. The exception is caught by the handler try/catch, so the message is nackd and retried — hitting the same path each time — meaning calendar reminder notifications silently fail to publish whenever tracing is inactive.

Fix: initialize Headers = new Dictionary<string, object>() in the BasicProperties initializer so the business headers never depend on trace injection having run. Zero tracing-behavior change — InjectTraceContexts ??= becomes a no-op and PublishTracedAsync still injects W3C context correctly.

Scope: SpikerSoft.EventHandlers.CalendarReminders/Services/CalendarReminderHostedService.cs. Build clean. (No unit-test project exists for this worker; a test would require standing up RabbitMQ IChannel + ActivitySource mocking scaffolding — noted as a follow-up rather than bundled into this surgical fix.) SonarQube rule external_roslyn:CS8602.

**Real latent NRE found via CS8602 triage** in the calendar-reminder publish path. `CalendarReminderHostedService.PublishNotificationEvent` builds `BasicProperties` **without** `Headers` (so `Headers` is null), then relies on `ActivityHelper.InjectTraceContext(properties, publishActivity)` to allocate `Headers` before writing the business-context headers (`event.id`, `user.id`). But `InjectTraceContext` returns early **without allocating `Headers`** when there is no ambient activity (`activity ??= Activity.Current; if (activity == null) return;`). That is exactly the case on a timer/consumer-driven publish where: - `_activitySource.StartActivity(...)` returns **null** because the `ActivitySource` has no registered listener (the failure mode `MessagePublishingSourceIsRegisteredTests` guards against), and - `Activity.Current` is also null (no ambient request trace). `Headers` then stays null and `properties.Headers["event.id"] = ...` throws an **NRE**. The exception is caught by the handler try/catch, so the message is nackd and retried — hitting the same path each time — meaning **calendar reminder notifications silently fail to publish whenever tracing is inactive**. **Fix:** initialize `Headers = new Dictionary<string, object>()` in the `BasicProperties` initializer so the business headers never depend on trace injection having run. Zero tracing-behavior change — `InjectTraceContext`s `??=` becomes a no-op and `PublishTracedAsync` still injects W3C context correctly. Scope: `SpikerSoft.EventHandlers.CalendarReminders/Services/CalendarReminderHostedService.cs`. Build clean. (No unit-test project exists for this worker; a test would require standing up RabbitMQ `IChannel` + `ActivitySource` mocking scaffolding — noted as a follow-up rather than bundled into this surgical fix.) SonarQube rule external_roslyn:CS8602.
Author
Owner

Resolved in spikersoft-backend PR #370 (merged to master). Initialized BasicProperties.Headers in the reminder publish so the business headers no longer NRE when tracing is inactive (StartActivity null + no ambient activity). Both-arch worker image built green; merged past the create_manifest packaging + notify steps. Closing.

Resolved in spikersoft-backend PR #370 (merged to `master`). Initialized BasicProperties.Headers in the reminder publish so the business headers no longer NRE when tracing is inactive (StartActivity null + no ambient activity). Both-arch worker image built green; merged past the create_manifest packaging + notify steps. Closing.
Author
Owner

Resolved in spikersoft-backend PR #366 (merged to master). Initialized BasicProperties.Headers so calendar-reminder business headers no longer NRE when tracing is inactive (CS8602). Build clean. All build/test contexts green; merged past the non-blocking notify job. Closing.

Resolved in spikersoft-backend PR #366 (merged to `master`). Initialized BasicProperties.Headers so calendar-reminder business headers no longer NRE when tracing is inactive (CS8602). Build clean. All build/test contexts green; merged past the non-blocking notify job. Closing.
Sign in to join this conversation.