Queue consumers are usually written for the optimistic failure: the downstream service was briefly unavailable, the database connection dropped, something timed out. Retry, and it works. What breaks that model is the message that cannot ever succeed — a malformed payload, a reference to a record that was deleted, a value that violates a constraint added after the message was enqueued. Retrying it produces the same failure at the same line, indefinitely, and the mechanism designed to add resilience becomes a loop that accomplishes nothing.

The damage depends on the queue’s shape. With strict ordering, a poison message is a wall: nothing behind it gets processed, and a single bad item stops the entire stream while the backlog grows behind it. Without ordering, it’s less dramatic but more insidious — the bad message cycles endlessly, consuming a worker slot and generating an error every few seconds, which for anyone reading the logs is indistinguishable from a system-wide problem. Either way the fix is the same in principle: after some number of attempts, stop trying and move the message somewhere else, so one item that can’t be processed doesn’t degrade the processing of everything else.

That somewhere else is where the second failure mode lives. Dead-letter queues are easy to create and easy to forget, and a dead-letter queue nobody looks at is just a slower way of dropping messages — with the added downside that everyone believes the work is recoverable because it’s technically still stored. The queue existing is not the safeguard. The safeguard is someone or something noticing that it’s non-empty, which means an alert on its depth, an owner, and a routine for triaging what lands there. Failing that, the honest description of the system is that it silently discards work it can’t handle, which may be acceptable but should at least be a decision rather than an accident.

The deeper question a poison message asks is what should happen to the work it represents. Sometimes it’s genuinely garbage and dropping it is correct. Sometimes it represents a real customer action — an order, a payment, a cancellation — and the right answer is that a human needs to know, because the business consequence of silently losing it is much larger than the engineering cost of surfacing it. Distinguishing those cases isn’t something a retry policy can do; it depends on what the message means, which is knowledge only the system’s designers have. A queue can tell you delivery failed. Only you can say whether that matters.

This is the same asymmetry the previous piece described from the other direction. Moving work off the request path buys latency at the cost of visibility, and the failure modes that were loud when synchronous become quiet when queued. A poison message is the sharpest version: entirely invisible in the metrics that look healthy — throughput fine, workers running, no user-facing error — while a specific piece of real work never happens at all, over and over, until someone thinks to go look.