Skip to main content
npayload is launching soon.
npayloadDocs
Concepts

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:

ConditionDescription
Retries exhaustedAll retry attempts failed (e.g., your endpoint returned 5xx on every attempt)
Circuit breaker openThe subscription's circuit breaker is open and the message cannot be queued further
Payload rejectedThe message payload was rejected by schema validation or exceeded size limits
Processing timeoutYour 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.

FieldDescription
gidUnique DLQ entry ID
originalMessageThe full original message (payload, metadata, routing key)
channelThe channel the message was published to
subscriptionIdThe subscription that failed to deliver
failureReasonWhy delivery failed (e.g., timeout, 5xx, circuit_breaker)
attemptCountTotal number of delivery attempts
firstAttemptAtTimestamp of the first delivery attempt
lastAttemptAtTimestamp of the last delivery attempt
createdAtWhen 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 caseHow the DLQ helps
DebuggingInspect the original message and failure reason to diagnose issues
Automated remediationSubscribe to the DLQ channel to trigger alerts or auto-healing workflows
Manual replayFix the root cause, then replay failed messages without re-publishing
ComplianceProve that no messages were lost, even during outages

Next steps

Was this page helpful?

On this page