oscerd opened a new pull request, #27834: URL: https://github.com/apache/camel/pull/27834
## Description [CAMEL-25660](https://issues.apache.org/jira/browse/CAMEL-25660): with `enableDeadLettering=true` in PEEK_LOCK mode, `ServiceBusConsumer.ConsumerOnCompletion.onFailure` dead-letters a failed message with a reason and description built from the route's exception. Two problems, the second the more serious. ### The call was unguarded ```java try { ... messageContext.deadLetter(deadLetterOptions); ... } finally { pendingExchanges.decrementAndGet(); } ``` No `catch`. When the broker rejects the request, the exception escapes `onFailure` into the Azure SDK's processor callback, where nothing reports it through the consumer's `ExceptionHandler`. The message stays locked, the lock expires, Service Bus redelivers it, the route fails the same way — until `maxDeliveryCount` is reached and the broker dead-letters it with its own `MaxDeliveryCountExceeded` reason. So the diagnostics `enableDeadLettering` exists to provide are exactly what gets lost, and the exchange is processed several more times than intended. ### Neither field was bounded To be clear, the *content* is what Azure asks for. [The dead-letter queue documentation](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dead-letter-queues) says: > We recommend that you include the type of the exception in the `DeadLetterReason` and the stack trace of the exception in the `DeadLetterDescription` as it makes it easier to troubleshoot the cause of the problem resulting in messages being dead-lettered. It might result in some messages exceeding the 256 KB quota limit for the Standard tier of Azure Service Bus. The component followed the recommendation but not the caveat. A full Camel stack trace, with error-handler and processor frames, is routinely several thousand characters, and `cause.getMessage()` is itself unbounded — a database error quoting the statement, a validation report. `com.azure.messaging.servicebus.models.DeadLetterOptions` does no client-side length validation (checked against azure-messaging-servicebus 7.17.16), so the broker is what rejects it, landing on the problem above. ## Change - Cap the reason at 1024 characters and the description at 8192, via `StringHelper.limitLength` with a trailing `...` so a reader of the dead-letter queue can tell the text was cut rather than that the trace ended there. The head of a stack trace carries the cause; the tail is error-handler frames. - Guard the dead-letter call and report a rejection through the consumer's exception handler. `onComplete` has the same unguarded shape around `messageContext.complete()`. I left it alone — it is a different failure (a lost lock rather than an oversized payload) and wants its own decision. Happy to fold it in if reviewers prefer. ## Tests Two tests added to `ServiceBusConsumerTest`, using the `handoverCompletions()` seam the existing dead-letter tests already use: - `synchronizationBoundsTheDeadLetterReasonAndDescription` — an exception with a 4096-character message and 512 stack frames; asserts both fields are within their caps and end with the ellipsis. - `synchronizationReportsARejectedDeadLetterInsteadOfThrowing` — `messageContext.deadLetter(any())` is stubbed to throw; asserts `onFailure` does not propagate it and the exception handler receives it. Revert-checked, each half separately: | reverted | result | |---|---| | the bounding | `synchronizationBoundsTheDeadLetterReasonAndDescription` fails on all 3 surefire runs | | the guard | `synchronizationReportsARejectedDeadLetterInsteadOfThrowing` errors on all 3 runs with `IllegalStateException: Dead-lettering rejected` propagating out of `onFailure` | | Module | Tests | |---|---| | camel-azure-servicebus | 76 (8 skipped) | No option metadata changed, so nothing needed regenerating. I did not run the full reactor; relying on CI for that. ## Docs Upgrade guide 4.24: the two caps, the ellipsis marker, and why — including that a rejected request used to leave the message to be redelivered. ## What is not verified Not reproduced against a live namespace: the exact payload size at which the broker rejects the request has not been measured, and Azure documents a message-size quota rather than a per-field limit. The caps are therefore a conservative choice rather than a protocol constant. The unguarded-call half does not depend on any of that — it is verified in Camel's own code and by the revert run above. An earlier version of CAMEL-25660 cited a 4096-character limit on these fields, from the .NET `DeadLetterMessageAsync` API reference. That is not corroborated by the dead-letter page or the quotas page, so I retracted it on the issue; the documented constraint is the 256 KB message quota quoted above. 🤖 Generated with [Claude Code](https://claude.com/claude-code) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
