Dead-Letter Queues: What to Do With Messages You Can't Process
Retries alone won't save a broken message. Learn how dead-letter queues catch poison messages, and how to triage, alert, and replay them safely.
Every queue-based system eventually meets a message it cannot process. A malformed payload, a reference to a record that was deleted, a currency amount encoded in a way your parser has never seen. Retries won't fix it — the message fails the same way on attempt one and attempt fifty. Without a plan, that single poison message either blocks the queue or silently vanishes. Both outcomes are bad. The plan is called a dead-letter queue.
What a Dead-Letter Queue Actually Is
A dead-letter queue (DLQ) is a second queue where messages go after they exhaust their retry budget. Instead of dropping the message or retrying forever, the broker (or your consumer code) moves it aside, preserving the full payload plus metadata: how many attempts were made, the last error, and timestamps.
The key insight is that a DLQ converts an unbounded runtime problem into a bounded operational one. Your main queue keeps flowing. The broken messages sit somewhere durable, waiting for a human or a repair job.
A DLQ is not a trash can. It's a hospital ward. Messages go there to be diagnosed and, usually, discharged.
Separate Transient Failures From Permanent Ones
Not every failure deserves the dead-letter treatment. Sort errors into two buckets before you dead-letter anything:
- Transient: timeouts, connection resets, a downstream service returning 503. These deserve retries with backoff, and most will succeed eventually.
- Permanent: validation errors, unparseable JSON, business-rule violations. Retrying is pointless — send these to the DLQ immediately, on the first failure.
Consumers that treat every exception identically waste retry cycles on hopeless messages and delay the queue for everyone behind them. A simple rule works well: classify known-permanent errors explicitly, and let everything else retry a small number of times (three to five) before dead-lettering.
Make the DLQ Impossible to Ignore
The most common DLQ failure mode is not technical — it's organizational. Teams wire up the queue, feel responsible, and then never look at it again. Six months later there are 40,000 messages in it and nobody knows which ones still matter.
Treat DLQ depth as a first-class alert:
- Alert on the first message, not on a threshold. One dead letter usually means a bug that will produce more.
- Include the error and payload summary in the alert so triage starts immediately.
- Set a retention policy long enough to survive a long weekend, short enough that the queue can't become a landfill.
Replay Deliberately, Not Hopefully
Once you've fixed the underlying bug, you'll want to replay dead-lettered messages back into the main queue. Do it carefully:
- Replay in small batches and watch the failure rate before draining everything.
- Make your consumers idempotent first — some dead-lettered messages may have partially succeeded before failing.
- Record which messages were replayed and when, so a second incident doesn't double-process them.
- If a message is genuinely obsolete (the order was cancelled, the account deleted), archive it with a reason instead of forcing it through.
The Takeaway
Retries handle the failures that heal themselves; dead-letter queues handle the ones that don't. Wire up a DLQ for every consumer, dead-letter permanent errors immediately, alert on the very first arrival, and build a boring, auditable replay path. The systems that survive bad data aren't the ones that never see it — they're the ones that have somewhere safe to put it.
Build with Abati Technology
We build software that ships — WhatsApp API, developer tools, POS, and mobile apps. Let's talk about your project.
Get in Touch →