Dead letter queue
Automatic capture of failed deliveries with inspection, replay, and purge
A dead letter queue (DLQ) captures messages that could not be delivered after all retry attempts have been exhausted. Instead of dropping failed messages, npayload stores them in the DLQ so you can inspect, debug, and replay them.
When messages enter the DLQ
A message is moved to the DLQ when any of the following conditions are met:
| Condition | Description |
|---|---|
| Retries exhausted | All retry attempts failed (e.g., your endpoint returned 5xx on every attempt) |
| Circuit breaker open | The subscription's circuit breaker is open and the message cannot be queued further |
| Payload rejected | The message payload was rejected by schema validation or exceeded size limits |
| Processing timeout | Your endpoint did not respond within the configured timeout on every attempt |
npayload never drops a message. If delivery fails, it goes to the DLQ. You can replay it at any time within the retention window.
DLQ types
Per-subscription DLQ
Every subscription has its own DLQ. Failed deliveries for that subscription are stored separately, making it easy to isolate and debug issues for a specific consumer.
const entries = await npayload.dlq.list({
subscriptionId: 'sub_abc123',
});System DLQ
The system DLQ captures messages that could not be routed to any subscription, or that failed at the channel level before fan-out.
const entries = await npayload.dlq.list({
scope: 'system',
});DLQ as a channel
The DLQ itself is a channel. You can create subscriptions on it to receive real-time notifications when messages fail, enabling automated alerting and remediation.
await npayload.subscriptions.create({
channel: '$dlq',
name: 'dlq-alerting',
type: 'webhook',
endpoint: {
url: 'https://alerts.example.com/dlq',
},
});DLQ entry structure
Each DLQ entry contains the original message along with failure details.
| Field | Description |
|---|---|
gid | Unique DLQ entry ID |
originalMessage | The full original message (payload, metadata, routing key) |
channel | The channel the message was published to |
subscriptionId | The subscription that failed to deliver |
failureReason | Why delivery failed (e.g., timeout, 5xx, circuit_breaker) |
attemptCount | Total number of delivery attempts |
firstAttemptAt | Timestamp of the first delivery attempt |
lastAttemptAt | Timestamp of the last delivery attempt |
createdAt | When the DLQ entry was created |
Inspecting entries
List and filter DLQ entries by subscription, channel, failure reason, or time range.
// List recent failures for a subscription
const entries = await npayload.dlq.list({
subscriptionId: 'sub_abc123',
limit: 50,
});
// Filter by failure reason
const timeouts = await npayload.dlq.list({
subscriptionId: 'sub_abc123',
failureReason: 'timeout',
});
// Get a single entry
const entry = await npayload.dlq.get('dlq_entry_456');Replaying failed deliveries
Replay a single entry or all entries for a subscription. Replayed messages go through the normal delivery pipeline, including retries.
// Replay a single entry
await npayload.dlq.replay('dlq_entry_456');
// Replay all entries for a subscription
await npayload.dlq.replayAll({
subscriptionId: 'sub_abc123',
});
// Replay entries matching a filter
await npayload.dlq.replayAll({
subscriptionId: 'sub_abc123',
failureReason: 'timeout',
});Before replaying, make sure the underlying issue is resolved (endpoint is healthy, schema is correct). Otherwise, replayed messages will fail again and re-enter the DLQ.
Purging
Remove DLQ entries that have been reviewed and are no longer needed.
// Purge a single entry
await npayload.dlq.purge('dlq_entry_456');
// Purge all entries for a subscription
await npayload.dlq.purgeAll({
subscriptionId: 'sub_abc123',
});Use cases
| Use case | How the DLQ helps |
|---|---|
| Debugging | Inspect the original message and failure reason to diagnose issues |
| Automated remediation | Subscribe to the DLQ channel to trigger alerts or auto-healing workflows |
| Manual replay | Fix the root cause, then replay failed messages without re-publishing |
| Compliance | Prove that no messages were lost, even during outages |
Next steps
- Subscriptions to understand delivery, retries, and circuit breakers
- Channels to learn about retention and channel types
- API reference for the full DLQ API
Was this page helpful?