Handling Poison Pills in Pub/Sub with Dead Letter Topics
Prevent 'poison pill' messages from blocking your Pub/Sub pipelines. Learn how to configure Dead Letter Topics, manage the required IAM permissions, and build a recovery workflow.
15 Jun 2026, 23:20 UTC

The Poison Pill Problem
In an asynchronous event-driven architecture, a "poison pill" is a message that cannot be processed successfully regardless of how many times it is retried. This usually happens due to a malformed payload, an unexpected null value, or a schema mismatch that triggers an unhandled exception in your subscriber code.
Without a strategy to isolate these messages, Pub/Sub will continue to redeliver the failing message based on your retry policy. This creates a loop that consumes CPU cycles, floods your logs with the same error, and can potentially block the processing of healthy messages if your subscriber has limited concurrency. The solution is a Dead Letter Topic (DLT): a designated destination where Pub/Sub automatically moves a message after it has failed a specific number of delivery attempts.
How Dead Lettering Works
A Dead Letter Topic is not a separate service, but a configuration on a Pub/Sub subscription. When you enable this feature, you define a deadLetterPolicy consisting of a target topic and a maxDeliveryAttempts threshold.
When a subscriber returns a NACK (negative acknowledgment) or the acknowledgment deadline expires, Pub/Sub increments the delivery attempt counter for that specific message. Once the counter hits your defined limit, Pub/Sub publishes the message to the dead letter topic and acknowledges it on the original subscription, effectively removing it from the primary pipeline.
IAM Requirements
A common point of failure during setup is missing permissions. Because the Pub/Sub service itself moves the message, the Pub/Sub Service Account (not your own user account) must have the pubsub.publisher role on the dead letter topic. Without this, messages will simply remain in the primary subscription and continue to fail.
Implementation Example
To implement a dead letter pattern, you need a primary topic for your data and a separate topic to capture failures. In this example, we assume you are using the gcloud CLI on a machine with the necessary project permissions.
- Create the Topics:
# Primary topic for application events gcloud pubsub topics create orders-topic # Topic for failed messages gcloud pubsub topics create orders-dead-letter-topic - Grant Permissions:
You must allow the Pub/Sub service agent to publish to the dead letter topic. Replace
PROJECT_NUMBERwith your numeric project ID.gcloud pubsub topics add-iam-policy-binding orders-dead-letter-topic \ --member="serviceAccount:[contact removed]" \ --role="roles/pubsub.publisher" - Create Subscription with Dead Letter Policy:
Set the threshold to 5 attempts. If a message fails 5 times, it moves to the DLT.
gcloud pubsub subscriptions create orders-sub \ --topic=orders-topic \ --dead-letter-topic=orders-dead-letter-topic \ --max-delivery-attempts=5
Verifying the Flow
To test this, publish a message to orders-topic and configure your subscriber to NACK every message it receives. After 5 delivery attempts, the message will disappear from orders-sub. You can verify its arrival in the DLT by pulling from a temporary subscription attached to orders-dead-letter-topic.
Trade-offs and Limitations
While dead lettering prevents pipeline stalls, it introduces new operational considerations:
- Transient vs. Permanent Failures: If you set
maxDeliveryAttemptstoo low (e.g., 1 or 2), a brief network blip or a database restart might push healthy messages into the dead letter topic, requiring manual intervention for a problem that would have resolved itself. - Ordering Guarantees: If you are using Ordering Keys, moving a message to a dead letter topic can break the sequence of events for that key, as subsequent messages for the same key may be processed while the failed message sits in the DLT.
- Silent Failures: Messages moving to a DLT happen in the background. Without monitoring, you may not realize your system is dropping 10% of its traffic.
Operationalizing the Recovery
A dead letter topic is only useful if you have a plan to handle the messages inside it. A production-ready pattern involves:
- Monitoring: Create a Cloud Monitoring alert on the
pubsub.googleapis.com/topic/send_request_countmetric for the dead letter topic. Any spike here indicates a deployment bug or a change in upstream data quality. - Inspection: Create a "Recovery Subscription" on the DLT. This allows engineers to pull a few messages, inspect the payload, and identify the cause of the failure.
- Reprocessing: Once the bug is fixed, write a simple script to pull messages from the DLT and republish them to the primary topic for reprocessing.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.