Dead-Letter Queue
A Dead-Letter Queue routes messages that fail processing after repeated attempts to a separate queue, keeping the main pipeline flowing while preserving failures for inspection and replay. It must be monitored and alerted on, and redriving requires idempotency to stay safe.
In an asynchronous messaging system, some messages simply cannot be processed: a malformed payload, a reference to deleted data, a bug that throws on every attempt. If the consumer keeps retrying such a message, it blocks the queue and starves every healthy message behind it. The Dead-Letter Queue (DLQ) pattern gives these messages somewhere to go: after a bounded number of failed attempts, the broker moves the message to a separate queue, and normal processing continues.
How It Works
The consumer (or broker) tracks delivery attempts per message. When a message exceeds the configured maximum retries — or fails validation outright — it is dead-lettered: published to a dedicated DLQ, usually with metadata recording the failure reason, original queue, and attempt count. The main queue then proceeds with the next message.
Most brokers support DLQs natively. Amazon SQS uses a redrive policy with maxReceiveCount; RabbitMQ supports dead-letter exchanges; Azure Service Bus and Kafka (via consumer logic or frameworks like Spring) provide equivalents. Operators monitor the DLQ, inspect failures, fix root causes (data or code), and then redrive — replay messages back to the main queue once the issue is resolved.
When to Use It
Use a DLQ in any at-least-once asynchronous pipeline where some messages may be permanently unprocessable: event-driven microservices, order/payment processing, ingestion pipelines, and integration flows. It is the standard companion to retry logic — retries handle transient failures, the DLQ catches the permanent ones.
Trade-offs
A DLQ that is not monitored becomes a silent black hole where data quietly accumulates and is lost; alerting on DLQ depth is mandatory. Redriving requires care to avoid re-triggering the same failure or, without idempotency, duplicating side effects from partially processed messages. Distinguishing transient from permanent failures (how many retries before dead-lettering) is a tuning decision. The DLQ also adds operational surface that teams must own.
Related Patterns
The DLQ is the destination for poison-message-handling, works after retry-with-backoff is exhausted, relies on idempotency-key for safe redrive, and may pair with the claim-check pattern for large payloads.
Example
An SQS redrive policy:
redrivePolicy: {
deadLetterTargetArn: orders-dlq,
maxReceiveCount: 5
}
After 5 failed receives, a message moves automatically to orders-dlq. An alarm on the DLQ's message count notifies on-call, who inspects, fixes the cause, and redrives the messages.