Skip to content

Dead Letter Queue

When a handler fails after all retries are exhausted, the failed message can be stored in a dead letter queue (DLQ) for later inspection and replay.

Enabling the DLQ

Set deadLetterQueue: true in the configuration:

const synkro = await Synkro.start({
transport: "redis",
connectionUrl: "redis://localhost:6379",
deadLetterQueue: true,
});

How it works

  1. A handler throws an error.
  2. Synkro retries according to the RetryConfig.
  3. After all retries are exhausted, the message is published as failed.
  4. If deadLetterQueue is enabled, a DeadLetterItem is pushed to the DLQ.

Inspecting failed messages

Retrieve DLQ items for a specific event type:

const items = await synkro.getDeadLetterItems("payment:charge");
for (const item of items) {
console.log(`Failed at: ${item.failedAt}`);
console.log(`Attempts: ${item.attempts}`);
console.log(`Errors:`, item.errors);
console.log(`Payload:`, item.payload);
}

You can limit the number of items returned:

const items = await synkro.getDeadLetterItems("payment:charge", { limit: 10 });

Replaying failed messages

Reprocess a failed message by passing it back through publish:

const items = await synkro.getDeadLetterItems("payment:charge");
for (const item of items) {
await synkro.replayDeadLetterItem(item);
}

replayDeadLetterItem re-publishes the event with the original eventType, payload, and requestId.

Clearing the DLQ

Remove all items from a specific event’s DLQ:

await synkro.clearDeadLetterQueue("payment:charge");

DeadLetterItem type

type DeadLetterItem = {
eventType: string;
requestId: string;
payload: unknown;
errors: Array<{ message: string; name?: string }>;
failedAt: string; // ISO 8601 timestamp
attempts: number; // total attempts (initial + retries)
};