diff --git a/Snippets/ASBS/ASBS_6/ASBS_6.csproj b/Snippets/ASBS/ASBS_6/ASBS_6.csproj
index f8ee3d00bf5..73167a00efb 100644
--- a/Snippets/ASBS/ASBS_6/ASBS_6.csproj
+++ b/Snippets/ASBS/ASBS_6/ASBS_6.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/Snippets/ASBS/ASBS_6/DeadLettering.cs b/Snippets/ASBS/ASBS_6/DeadLettering.cs
new file mode 100644
index 00000000000..7b17d6f2850
--- /dev/null
+++ b/Snippets/ASBS/ASBS_6/DeadLettering.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using NServiceBus;
+using NServiceBus.Transport.AzureServiceBus;
+
+class DeadLettering
+{
+ void MoveErrorsToDeadLetterQueue(EndpointConfiguration endpointConfiguration)
+ {
+ #region dlq-all-errors
+
+ endpointConfiguration.Recoverability()
+ .MoveErrorsToAzureServiceBusDeadLetterQueue();
+
+ #endregion
+ }
+
+ void ExplicitDlq(EndpointConfiguration endpointConfiguration)
+ {
+ #region explicit-dlq
+
+ endpointConfiguration.Recoverability()
+ .CustomPolicy((config, errorContext) =>
+ {
+ if (errorContext.Exception is PoisonMessageException)
+ {
+ return RecoverabilityAction.DeadLetter();
+ }
+
+ return DefaultRecoverabilityPolicy.Invoke(config, errorContext);
+ });
+
+ #endregion
+ }
+
+ void ExplicitDlqFullControl(EndpointConfiguration endpointConfiguration)
+ {
+ #region explicit-dlq-full-control
+
+ endpointConfiguration.Recoverability()
+ .CustomPolicy((config, errorContext) =>
+ {
+ if (errorContext.Exception is MyBusinessException ex)
+ {
+ return RecoverabilityAction.DeadLetter(
+ deadLetterReason: "Business rule validation failed",
+ deadLetterErrorDescription: ex.Message,
+ propertiesToModify: new Dictionary
+ {
+ ["FailureCategory"] = "Validation"
+ });
+ }
+
+ return DefaultRecoverabilityPolicy.Invoke(config, errorContext);
+
+ });
+ #endregion
+ }
+
+ class PoisonMessageException : Exception
+ {
+ }
+
+ class MyBusinessException : Exception
+ {
+ }
+}
\ No newline at end of file
diff --git a/Snippets/ASBS/ASBS_6/Usage.cs b/Snippets/ASBS/ASBS_6/Usage.cs
index 1b5ed398c36..52e3a19a3f5 100644
--- a/Snippets/ASBS/ASBS_6/Usage.cs
+++ b/Snippets/ASBS/ASBS_6/Usage.cs
@@ -146,6 +146,10 @@ class Usage
// exclude all types that inherit an interface or base type
transport.HierarchyNamespaceOptions.ExcludeMessageType();
#endregion
+
+ #region enable-dlq-auto-forwarding
+ transport.AutoForwardDeadLetteredMessagesToErrorQueue = true;
+ #endregion
}
@@ -155,7 +159,7 @@ public class MyExcludedMessage {}
public interface IAmExcludedFromTheHierarchy {}
public class MyExcludedMessageByInterface : IAmExcludedFromTheHierarchy { }
-
+
public class MyOtherExcludedMessageByInterface : IAmExcludedFromTheHierarchy {}
#endregion
class MyEvent;
diff --git a/transports/azure-service-bus/configuration.md b/transports/azure-service-bus/configuration.md
index 18785ec4a6d..8d71b589d55 100644
--- a/transports/azure-service-bus/configuration.md
+++ b/transports/azure-service-bus/configuration.md
@@ -73,3 +73,5 @@ partial: lockrenewal
>
> - Optimise the message handlers to reduce their execution time.
> - Reduce the prefetch count. All messages are locked on peek, so when they are prefetched, they remain locked until they are all processed.
+
+partial: dead-lettering
diff --git a/transports/azure-service-bus/configuration_dead-lettering_asbs_[6,).partial.md b/transports/azure-service-bus/configuration_dead-lettering_asbs_[6,).partial.md
new file mode 100644
index 00000000000..e2999f4565c
--- /dev/null
+++ b/transports/azure-service-bus/configuration_dead-lettering_asbs_[6,).partial.md
@@ -0,0 +1,58 @@
+## Dead lettering
+
+> [!NOTE]
+> Support for dead lettering is available in Version 6.3.0 and higher.
+
+Azure Service Bus provides a native dead-letter queue (DLQ) for each queue and subscription. NServiceBus can integrate with this mechanism, allowing failed messages to be dead-lettered natively and forwarded to the central NServiceBus error queue.
+
+For background information on Azure Service Bus dead-letter queues, see [Overview of Service Bus dead-letter queues](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dead-letter-queues).
+
+### Forward dead-lettered messages to the error queue
+
+When queues are created by the transport, native dead-lettered messages can be auto-forwarded to the configured error queue:
+
+snippet: enable-dlq-auto-forwarding
+
+This setting is opt-in and affects only queues created by the transport. See the [`asb-transport` provisioning commands](/transports/azure-service-bus/operational-scripting.md) for more details on scripting options.
+
+### Route all failed messages to the native DLQ
+
+To route failed messages to the native Azure Service Bus dead-letter queue instead of the NServiceBus error queue, enable:
+
+snippet: dlq-all-errors
+
+### Request dead lettering from recoverability
+
+Use a [custom recoverability policy](/nservicebus/recoverability/custom-recoverability-policy.md) to explicitly request dead lettering for selected failures.
+
+Dead-letter with standard NServiceBus fault metadata:
+
+snippet: explicit-dlq
+
+Dead-letter with custom reason, description, and modified application properties:
+
+> [!NOTE]
+> Using this option does note automatically add the NServiceBus faults metadata to the application properties.
+
+snippet: explicit-dlq-full-control
+
+### Fault header mapping
+
+When processing dead-lettered messages, the transport maps native dead-letter properties to [error forwarding headers](/nservicebus/messaging/headers.md#error-forwarding-headers) when those headers are not already present:
+
+- `DeadLetterSource` -> `NServiceBus.FailedQ`
+- `DeadLetterReason` -> `NServiceBus.ExceptionInfo.Message`
+- `DeadLetterErrorDescription` -> `NServiceBus.ExceptionInfo.StackTrace`
+
+This mapping helps tools such as ServiceControl and ServicePulse present failure information consistently.
+
+### Monitoring and operations
+
+[ServicePulse failed message monitoring](/servicepulse/intro-failed-messages.md) tracks messages in the NServiceBus error queue. If endpoint failures are kept in native Azure Service Bus dead-letter queues without forwarding, those failures require Azure-native operational tooling.
+
+Enable DLQ forwarding as described above when you want to centralized native dead lettering and failed-message handling.
+
+### Caveats
+
+- `TransportTransactionMode.None` uses receive-and-delete semantics, so dead-lettering actions cannot be performed in that mode. See [transport transactions](/transports/transactions.md#transaction-modes-unreliable-transactions-disabled).
+- The transport truncates dead-letter reason and description to 1024 characters to reduce oversized message risk. Review Azure limits in [Service Bus quotas](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-quotas).
diff --git a/transports/azure-service-bus/configuration_entity-settings_asbs_[6,).partial.md b/transports/azure-service-bus/configuration_entity-settings_asbs_[6,).partial.md
index 6d7c6d2b2a4..bffef8a1800 100644
--- a/transports/azure-service-bus/configuration_entity-settings_asbs_[6,).partial.md
+++ b/transports/azure-service-bus/configuration_entity-settings_asbs_[6,).partial.md
@@ -3,4 +3,7 @@
* `EntityMaximumSize`: The maximum entity size in GB. The value must correspond to a valid value for the namespace type. Defaults to 5. See [the Microsoft documentation on quotas and limits](https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-quotas) for valid values.
* `EnablePartitioning`: Partitioned entities offer higher availability, reliability, and throughput over conventional non-partitioned queues and topics. For more information about partitioned entities [see the Microsoft documentation on partitioned messaging entities](https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-partitioning).
* `AutoDeleteOnIdle`: A `TimeSpan` representing the [AutoDeleteOnIdle](https://learn.microsoft.com/en-us/azure/service-bus-messaging/advanced-features-overview#autodelete-on-idle) setting for instance-specific input queues (such as when using [`MakeInstanceUniquelyAddressable`](/nservicebus/messaging/routing.md#make-instance-uniquely-addressable)) created by the transport. This value is the maximum time period that a queue can remain [idle](https://learn.microsoft.com/en-us/azure/service-bus-messaging/message-expiration#idleness) before Azure Service Bus automatically deletes it. Defaults to `TimeSpan.MaxValue` in Azure Service Bus if this setting is not specified within the transport. The minimum allowed value is 5 minutes. The transport will not apply this setting to topics or subscriptions as these are considered shared infrastructure (along with shared queues such as error and audit).
+* `AutoForwardDeadLetteredMessagesToErrorQueue`: When enabled, dead-lettered messages from transport-created receive queues are auto-forwarded to the configured NServiceBus error queue. It's recommended to enable this option to centralize failed-message handling for dead-letter queues. For general Azure Service Bus forwarding behavior, see [Enable auto forwarding for Azure Service Bus queues and subscriptions](https://learn.microsoft.com/en-us/azure/service-bus-messaging/enable-auto-forward). This setting is nullable; when not explicitly configured, a warning is logged at runtime if a message is dead-lettered.
* `HierarchyNamespaceOptions`: Beginning version 6.1, [hierarchical entities](https://learn.microsoft.com/en-us/rest/api/servicebus/addressing-and-protocol) can be [configured using a hierarchical namespace](#entity-creation-hierarchy-namespace). Setting this with the required `HierarchyNamespace` property will prefix all entity paths in the format `{HierarchyNamespace}/{original-entity-path}`. Defaults to `HierarchyNamespaceOptions.None`, which disables prefixing with a hierarchy namespace.
+* `MaxDeliveryCount`: The maximum delivery count applied to queues created during endpoint infrastructure setup. Defaults to `int.MaxValue`, which effectively disables Azure Service Bus's built-in delivery count limits and defers all retry decisions to NServiceBus recoverability. The Azure Service Bus Emulator requires a value of `10`. When choosing a value, ensure it is high enough to allow the configured recoverability policy to eventually move the message to the error queue, but not so high that it creates effectively infinite retries.
+* `ThrowOnMissingTopicWhenPublishing`: When enabled, the transport re-throws the underlying `ServiceBusException` when publishing to a non-existent topic. The transport always logs a warning when publishing to a non-existent topic. Defaults to `false` for backward compatibility.
diff --git a/transports/azure-service-bus/operational-scripting_endpoint-command_asbs_[6,).partial.md b/transports/azure-service-bus/operational-scripting_endpoint-command_asbs_[6,).partial.md
index 0dd5b7cc42c..d00040b93b7 100644
--- a/transports/azure-service-bus/operational-scripting_endpoint-command_asbs_[6,).partial.md
+++ b/transports/azure-service-bus/operational-scripting_endpoint-command_asbs_[6,).partial.md
@@ -6,6 +6,7 @@ Creates infrastructure for an endpoint -- input queue.
asb-transport endpoint create name
[--size]
[--partitioned]
+ [--forward-dlq-to]
```
#### options
@@ -20,6 +21,8 @@ asb-transport endpoint create name
`-h` | `--hierarchy-namespace`: Sets the hierarchy namespace for prefixing destinations in the format `/` (available from version 6.1)
+`-f` | `--forward-dlq-to`: Queue name to auto-forward dead-lettered messages to. The queue will be created if it does not exist. The resolved queue name cannot be the same as the endpoint queue.
+
### asb-transport endpoint subscribe
Creates a new subscription for an endpoint.
diff --git a/transports/azure-service-bus/operational-scripting_examples_asbs_[5,).partial.md b/transports/azure-service-bus/operational-scripting_examples_asbs_[5,).partial.md
index 596e7e470ef..9684c8c32d0 100644
--- a/transports/azure-service-bus/operational-scripting_examples_asbs_[5,).partial.md
+++ b/transports/azure-service-bus/operational-scripting_examples_asbs_[5,).partial.md
@@ -2,21 +2,33 @@
Create the topology for an endpoint named `MyEndpoint`:
-```
+```txt
asb-transport endpoint create MyEndpoint -c ""
```
-### Subscribing to events
+Create the topology for an endpoint named `MyEndpoint` and forward dead-lettered messages to the `error` queue:
-Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` using the default settings:
+```txt
+asb-transport endpoint create MyEndpoint --forward-dlq-to error -c ""
+```
+
+Create a queue and configure dead-letter forwarding to the `error` queue:
+```txt
+asb-transport queue create MyEndpoint --forward-dlq-to error -c ""
```
+
+### Subscribing to events (migration topology)
+
+Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` using the default settings:
+
+```txt
asb-transport endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -c ""
```
Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` and override the subscription name to be `my-other-endpoint`
-```
+```txt
asb-transport endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -s my-other-endpoint -c ""
```
@@ -24,19 +36,25 @@ asb-transport endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -s m
Create the topology for an endpoint named `MyEndpoint` using the default settings:
-```
+```txt
asb-transport migration endpoint create MyEndpoint -c ""
```
-Create the topology for an endpoint named `MyEndpoint` and override the topic name to be `custom-topic` and the subscription name to be `my-endpoint`:
+Create migration topology and forward dead-lettered messages to the `error` queue:
+```txt
+asb-transport migration endpoint create MyEndpoint --forward-dlq-to error -c ""
```
+
+Create the topology for an endpoint named `MyEndpoint` and override the topic name to be `custom-topic` and the subscription name to be `my-endpoint`:
+
+```txt
asb-transport migration endpoint create MyEndpoint -t custom-topic -s my-endpoint -c ""
```
Create the topology for an endpoint named `MyEndpoint` and override the publish topic name to be `custom-publish-topic` and the subscription topic name to be `custom-subscribe-topic`:
-```
+```txt
asb-transport migration endpoint create MyEndpoint -tp custom-publish-topic -ts custom-subscribe-topic -c ""
```
@@ -44,30 +62,30 @@ asb-transport migration endpoint create MyEndpoint -tp custom-publish-topic -ts
Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` using the default settings:
-```
+```txt
asb-transport migration endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -c ""
```
Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` using the topic-per-event apprach (after migration):
-```
+```txt
asb-transport migration endpoint subscribe-migrated MyOtherEndpoint Contracts.Events.SomeEvent -c ""
```
Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` and override the topic name to be `custom-topic`:
-```
+```txt
asb-transport migration endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -t custom-topic -c ""
```
Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` and override the subscription name to be `my-other-endpoint`
-```
+```txt
asb-transport migration endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -s my-other-endpoint -c ""
```
Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` and override the subscription rule name to be `SomeEvent`:
-```
+```txt
asb-transport migration endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -r SomeEvent -c ""
-```
\ No newline at end of file
+```
diff --git a/transports/azure-service-bus/operational-scripting_migration-endpoint-command_asbs_[6,).partial.md b/transports/azure-service-bus/operational-scripting_migration-endpoint-command_asbs_[6,).partial.md
index 80969fa9198..ea2602d0b9b 100644
--- a/transports/azure-service-bus/operational-scripting_migration-endpoint-command_asbs_[6,).partial.md
+++ b/transports/azure-service-bus/operational-scripting_migration-endpoint-command_asbs_[6,).partial.md
@@ -19,6 +19,7 @@ asb-transport migration endpoint create name
[--topic]
[--topic-to-publish-to] [--topic-to-subscribe-on]
[--subscription]
+ [--forward-dlq-to]
```
#### options
@@ -41,6 +42,8 @@ asb-transport migration endpoint create name
`-h` | `--hierarchy-namespace`: Sets the hierarchy namespace for prefixing destinations in the format `/` (available from version 6.1)
+`-f` | `--forward-dlq-to`: Queue name to auto-forward dead-lettered messages to. The queue will be created if it does not exist. The resolved queue name cannot be the same as the endpoint queue.
+
> [!NOTE]
> The hierarchy namespace option shifts the migration endpoint consistently into the hierarchy meaning the endpoint name and topics will have the hierarchy name applied.
diff --git a/transports/azure-service-bus/operational-scripting_queue-command_asbs_[6,).partial.md b/transports/azure-service-bus/operational-scripting_queue-command_asbs_[6,).partial.md
index 12eb2aa63f9..7a852ad504d 100644
--- a/transports/azure-service-bus/operational-scripting_queue-command_asbs_[6,).partial.md
+++ b/transports/azure-service-bus/operational-scripting_queue-command_asbs_[6,).partial.md
@@ -6,6 +6,7 @@ Create a queue using:
asb-transport queue create name
[--size]
[--partitioned]
+ [--forward-dlq-to]
```
#### options
@@ -20,6 +21,10 @@ asb-transport queue create name
`-h` | `--hierarchy-namespace`: Sets the hierarchy namespace for prefixing destinations in the format `/` (available from version 6.1)
+`-f` | `--forward-dlq-to`: Queue name to auto-forward dead-lettered messages to. The queue will be created if it does not exist. The resolved queue name cannot be the same as the source queue.
+
+See also the Azure CLI option [`--forward-dead-lettered-messages-to`](https://learn.microsoft.com/en-us/cli/azure/servicebus/queue?view=azure-cli-latest#az-servicebus-queue-create).
+
### asb-transport queue delete