// Resilience & Messaging · 07
Dead-Letter Queue
Where messages go after too many failed attempts, to be looked at.
How it fits together
- Orders queue publishes / notifies Order consumer (1 deliver)
- Order consumer fails / error path Orders queue (2 retry x5)
- Order consumer fails / error path Dead-letter queue (3 after 5th)
- Dead-letter queue publishes / notifies Alert (4 alert)
- Ops engineer uses / calls Dead-letter queue (5 inspect)
- Ops engineer creates Orders queue (6 resubmit)
Spot it
- One bad message blocks the whole queue
- The same message fails in a loop for hours
- Messages vanish after the retry limit
It costs
A DLQ only helps if someone watches it and can replay what lands there.
In code
public sealed class OrderConsumer(IOrderStore orders, IDeadLetterSink deadLetters){ private const int MaxDeliveries = 5; public async Task HandleAsync(Delivery delivery, CancellationToken ct) { try { var order = OrderMessage.Parse(delivery.Body); await orders.SaveAsync(order, ct); await delivery.CompleteAsync(ct); } catch (ValidationException ex) { await deadLetters.SendAsync(delivery, $"invalid payload: {ex.Message}", ct); } catch (Exception ex) when (delivery.DeliveryCount >= MaxDeliveries) { await deadLetters.SendAsync(delivery, $"max deliveries reached: {ex.Message}", ct); } catch (Exception) { await delivery.AbandonAsync(ct); // back on the queue for another try } }}Go deeperThe full explanation and what to ask for · 1 min readExpandCollapse
A poison message fails every time: malformed payload, a bug, missing reference data. Without a limit it is redelivered forever, burns capacity and, on ordered queues or sessions, blocks everything behind it. Brokers move it aside after a max delivery count (Azure Service Bus defaults to 10; SQS calls it maxReceiveCount in its redrive policy).
The classic mistake is a DLQ nobody monitors, which is just a slower way to lose data. Alert on its depth, record why each message was dead-lettered, and build redrive tooling before you need it.
Separate transient from permanent failures: retry transient ones with backoff, dead-letter permanent ones at once. Redriven messages arrive a second time, so consumers must be idempotent.
Ask for it
- poison message
- max delivery count
- redrive
- dead-letter reason
- visibility timeout
On the orders queue, set the max delivery count to 5, dead-letter validation failures immediately with a reason, alert when the dead-letter queue is non-empty for 10 minutes, and add a command that inspects and resubmits dead-lettered messages after a fix.