You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Handler-rejected Azure Service Bus messages are dead-lettered with blankDeadLetterReason and DeadLetterErrorDescription. The real reason exists only in logs, so DLQ triage means pivoting to log search and correlating by sequence number / message id.
Root cause
AzureServiceBusConsumer.RejectAsync computes the reason and description, logs them, then settles with the no-reason overload:
git blame: these fields were introduced in #3918 (Universal DLQ) and wired only into the log line; the no-reason settle predates it and was never updated. Sync Reject delegates here via BrighterAsyncContext.Run, so it shares the behaviour. This is the only Brighter-initiated dead-letter call site — ASB-native auto-deadlettering (TTL expiry, max-delivery-count) already populates the columns itself.
Present on 10.4.1 and master. The SDK (Azure.Messaging.ServiceBus 7.20.1) already ships DeadLetterMessageAsync(message, deadLetterReason, deadLetterErrorDescription, ct) (4096-char cap, requires PeekLock — which Brighter uses); the gateway just never calls it. Consistent with ADR 0045-provide-dlq-where-missing ("defer to native") — this makes the deferral non-lossy rather than pulling ASB into the produced-message Universal DLQ path.
Proposed fix
Add a forwarding overload to IServiceBusReceiverWrapper, implement it via the existing CreateMessageShiv pattern (as Complete/DeadLetter/Abandon do) with 4096-char truncation, and change the single settle call to pass reasonString/description. Result: DeadLetterReason ← enum name, DeadLetterErrorDescription ← description text. Test-first, covering async + sync paths.
The overload must go on the publicIServiceBusReceiverWrapper (the consumer calls through it) — a breaking change for external implementers. A default interface method isn't viable (netstandard2.0 is in the target matrix). Accept the interface addition, or prefer a different shape?
Symptom
Handler-rejected Azure Service Bus messages are dead-lettered with blank
DeadLetterReasonandDeadLetterErrorDescription. The real reason exists only in logs, so DLQ triage means pivoting to log search and correlating by sequence number / message id.Root cause
AzureServiceBusConsumer.RejectAsynccomputes the reason and description, logs them, then settles with the no-reason overload:git blame: these fields were introduced in #3918 (Universal DLQ) and wired only into the log line; the no-reason settle predates it and was never updated. SyncRejectdelegates here viaBrighterAsyncContext.Run, so it shares the behaviour. This is the only Brighter-initiated dead-letter call site — ASB-native auto-deadlettering (TTL expiry, max-delivery-count) already populates the columns itself.Present on
10.4.1andmaster. The SDK (Azure.Messaging.ServiceBus7.20.1) already shipsDeadLetterMessageAsync(message, deadLetterReason, deadLetterErrorDescription, ct)(4096-char cap, requires PeekLock — which Brighter uses); the gateway just never calls it. Consistent with ADR0045-provide-dlq-where-missing("defer to native") — this makes the deferral non-lossy rather than pulling ASB into the produced-message Universal DLQ path.Proposed fix
Add a forwarding overload to
IServiceBusReceiverWrapper, implement it via the existingCreateMessageShivpattern (asComplete/DeadLetter/Abandondo) with 4096-char truncation, and change the single settle call to passreasonString/description. Result:DeadLetterReason← enum name,DeadLetterErrorDescription← description text. Test-first, covering async + sync paths.Questions
@iancooper:
IServiceBusReceiverWrapper(the consumer calls through it) — a breaking change for external implementers. A default interface method isn't viable (netstandard2.0is in the target matrix). Accept the interface addition, or prefer a different shape?