On this page
  1. Why do messages get stuck in a queue?
  2. How does a dead letter queue fix this?
  3. What should you do with messages in the DLQ?
  4. Where AI coding assistants get this wrong
  5. Checklist
  6. FAQ
    1. What’s the difference between a DLQ and just logging failures?
    2. How many retries should happen before a message goes to the DLQ?
  7. Related topics
  8. Sources
concept

What Is a Dead Letter Queue?

A dead letter queue (DLQ) collects messages that a queue repeatedly fails to process. Learn why you need one and what to do with its contents.

Quick answer

  • A dead letter queue is a separate queue that receives messages a worker keeps failing to process.
  • It stops one poisoned message from blocking the whole queue and preserves the message for debugging.
  • Without a DLQ, a permanently failing message is retried forever and quietly dropped when its retention expires.

Why do messages get stuck in a queue?

A worker pulls a message and tries to process it — but the task fails: a malformed payload, a missing record, a downstream API that’s permanently broken for this item. If the worker doesn’t acknowledge the message, the queue redelivers it, and the cycle repeats. One bad message can starve the queue: every retry burns worker time, delays other work, and inflates your compute bill.

How does a dead letter queue fix this?

You configure the source queue to move a message to the DLQ after a set number of failed receives (for example, three). The source queue keeps flowing; the failed message is parked in the DLQ with its original payload and metadata intact. You then handle DLQ contents deliberately: inspect them, fix the bug or data, and optionally replay them into the source queue. In SQS this is a source queue attribute; in RabbitMQ you configure a DLX (dead letter exchange) binding.

# AWS SQS: attach a DLQ via the AWS CLI
export QUEUE_URL=$(aws sqs create-queue --queue-name jobs --attributes '{"RedrivePolicy":"{\"deadLetterTargetArn\":\"arn:aws:sqs:...:jobs-dlq\",\"maxReceiveCount\":3}"}' --query QueueUrl --output text)

What should you do with messages in the DLQ?

Alert on DLQ depth — a growing DLQ is an incident signal, not a storage bin. Inspect samples of the payload to find the pattern (is it always the same user, same data shape?). Fix the code or data, then replay the valid messages into the source queue. Messages that are genuinely unrecoverable get archived or deleted after you’ve learned from them.

Where this bites vibecoders

AI assistants happily generate queue workers but almost never generate DLQs, alerting, or replay tooling. The result: a ‘reliable’ job queue that silently loses messages after retries expire. Adding a DLQ plus an alert on its depth is one of the highest-value reliability upgrades a small system can get, and it’s exactly the part of the architecture the assistant won’t volunteer.

Where AI coding assistants get this wrong

  • Writing workers that retry forever with no max-attempts and no DLQ, blocking the queue.
  • Skipping alerting on DLQ depth, so failures pile up invisibly.
  • Using a DLQ but no monitoring or replay path, turning it into a data graveyard.
  • Putting the DLQ on the same infrastructure as the source queue, so one outage kills both.

Checklist

  • Configure a DLQ with a sane max-receive count on every production queue.
  • Alert when DLQ depth exceeds zero for more than a few minutes.
  • Document how to inspect and replay DLQ messages.
  • Test the DLQ path once: push a poisoned message and watch it move.

FAQ

What’s the difference between a DLQ and just logging failures?

Logging records that a failure happened; a DLQ preserves the actual message payload so it can be inspected and replayed. Logs are often truncated or rotated, while a DLQ keeps the exact input that broke processing.

How many retries should happen before a message goes to the DLQ?

Enough to ride out transient failures, not so many that the queue backs up. Three to five is a common starting point for idempotent workers. What matters more is that the DLQ exists and is alerted on — the exact count is tunable.

Sources

Share: