Back to blog

Your DLQ Has Been Green for Three Weeks — And That's Exactly the Problem

Sep 12, 2026
Series · Day 10
One Concept a Day — The AI-Era Engineer's Glossary
View all lessons →
Your DLQ Has Been Green for Three Weeks — And That's Exactly the Problem

Why this matters

Every team with a message queue eventually stands up a DLQ. Almost nobody builds the other half — the part that gets messages back out. So the DLQ quietly turns into a second graveyard for your data, just one with a delay on it.

The incident

Picture the dashboard: DLQ panel, green, zero alerts. Nobody's been paged in weeks — which is exactly the problem. Then finance asks where the refund events from three weeks back went. You check. They landed in the DLQ during a downstream outage, sat there untouched, and aged out at the 14-day retention mark. The queue didn't fail — it did precisely what it was configured to do. The failure was that nobody ever wired up a way to pull messages out before the clock ran out.

What a DLQ actually is

A Dead Letter Queue is a holding pen for messages a consumer couldn't process after exhausting its retries, or that failed validation outright. One-sentence definition for this series: a DLQ is where a message goes when your system gives up on it automatically — not where the problem gets solved, just where it gets parked.

It exists so one poison message — malformed JSON, a null field your code never expected, a timeout — doesn't jam the whole queue or vanish silently. Instead of the consumer loop crashing forever, the message gets shoved aside and processing keeps moving. That's the entire job of a DLQ. Nothing about 'catch' implies 'recover.'

The uncomfortable truth

'We have a DLQ' is a vanity metric. It answers 'do we lose messages silently?' with a no — it says nothing about whether you lose messages at all. Most teams stop at the catch: wire up a redrive policy, point failures at a dead-letter topic, ship it, move on. Almost nobody builds the release half — the triage step that looks at what's actually sitting in there, the replay path that puts it back into the main flow, or the quarantine for messages that are genuinely poison and will never succeed no matter how many times you retry them. A DLQ without a replay path isn't a safety net. It's a queue with a delay-fuse on data loss, and the fuse length is whatever your retention policy says it is.

Anatomy of the missing half

  • Alert on depth AND age. Depth tells you something's wrong right now. Age tells you a message is close to falling off the retention cliff — a DLQ holding 3 messages for 13 days is a bigger emergency than one holding 300 messages that landed 10 minutes ago and are still perfectly retryable.
  • Build triage that actually classifies transient vs. poison. A downstream 503 during a deploy is transient — replay it. A message carrying a schema no version of your consumer has ever supported is poison — quarantine it and stop blindly retrying.
  • Get a replay mechanism you've actually tested. Not a runbook line that says 'requeue from DLQ,' but a script or job someone has run in staging — ideally in a game day — so the first time it runs for real isn't during the finance escalation.
  • Assign an owner and a cadence. A name next to 'reads this queue,' and a stated frequency — not an implicit assumption that whoever built it three years ago is still watching.

The agentic twist

In an agent pipeline, the thing failing isn't a malformed payload anymore — it's a tool call. Your agent tries to invoke `issue_refund` with a hallucinated account ID. Or a call times out because the model generated a param the schema doesn't allow. Or a multi-step chain dies three hops in because step two returned something step three couldn't parse. That failed tool call lands in your version of a DLQ — whatever you're using to capture it, a table, a queue, a trace store.

The triage problem gets harder here, not easier. A stack trace tells you where the code broke. It tells you nothing about why the model chose that param, whether the prompt was ambiguous, or whether this is the fifth time this week the same tool got misused the same way. Real triage for agentic failures needs a human-readable reason attached at the point of failure — 'model attempted to call refund with a negative amount,' not just 'ValidationError at line 214' — because whoever (or whatever) is doing the triage needs to reason about intent, not just syntax. If your agent framework can't tell you what the model was trying to do when it failed, you've built a DLQ for a system you can't actually triage.

The reframe

A DLQ with no replay path isn't a safety net — it's a delayed data-loss incident with your retention window as the countdown timer. The fix isn't a new tool or a bigger platform investment. It's a one-line policy question every team should be asking this week: who reads this queue, and how often? No answer means you don't have a DLQ. You have a very patient trash can.

Flashcards
Check yourself

Extend your knowledge

  • Pull up your own DLQ — SQS, a Kafka dead-letter topic, RabbitMQ's DLX — and check two numbers right now: the current retention period, and the timestamp on the oldest message. That gap is your actual risk window.
  • Running agent pipelines? Check whether your failed tool-call logs capture the model's intended action in plain language, or only the raised exception. That gap is your triage blind spot.
  • Read AWS's own documentation on SQS dead-letter queues and redrive policies — it's a clean model for depth/age alerting even if you're nowhere near SQS.
  • Run a game day. Manually trigger a downstream failure, let messages land in the DLQ, and time how long it takes a human to notice, triage, and replay them. That number is your real recovery time — not the one written in the runbook.
Test yourself on this lesson

Discussion

Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.

Ask me anything about “Your DLQ Has Been Green for Three Weeks — And That's Exactly the Problem” — trade-offs, decisions, or the story behind it.