Skip to main content
The @DeadLetterQueue annotation enables automatic routing of failed events to a dedicated MongoDB collection. When a handler throws an exception — and all retries are exhausted if @RetryPolicy is present — the event is persisted in the DLQ instead of being silently lost.

Attributes

AttributeTypeDefaultDescription
enabledbooleantrueWhether DLQ is enabled
collectionString"_fw_dlq"MongoDB collection name for the DLQ
ttlDaysint30Time-to-live in days. 0 means permanent (no expiry)
includeOriginalDocumentbooleantrueWhether to include the original document in the DLQ entry
includeStackTracebooleantrueWhether to include the full stack trace in the DLQ entry

collection

The MongoDB collection where failed events are stored. All streams sharing the same collection value write to the same DLQ collection — events are distinguished by their streamName field.
// Use a dedicated DLQ collection for this stream
@DeadLetterQueue(collection = "orders_dlq")

ttlDays

Controls automatic cleanup of DLQ entries via MongoDB’s TTL mechanism. Set to 0 to keep entries permanently.
// Keep failed events for 90 days
@DeadLetterQueue(ttlDays = 90)

// Keep failed events permanently (no auto-cleanup)
@DeadLetterQueue(ttlDays = 0)

includeOriginalDocument and includeStackTrace

These control what data is captured in the DLQ entry. Disabling them can reduce storage for high-throughput streams where you only need the error metadata.
// Minimal DLQ entry — only metadata, no document or stack trace
@DeadLetterQueue(includeOriginalDocument = false, includeStackTrace = false)

DLQ Storage SPI

The DlqStore interface is the SPI that backs @DeadLetterQueue. The MongoDB implementation is auto-configured, but you can provide your own by registering a DlqStore bean.
public interface DlqStore {

    /** Persists a failed event into the DLQ. */
    void save(FailedEvent event);

    /** Retrieves a failed event by its unique identifier. */
    Optional<FailedEvent> findById(String id);

    /** Retrieves all failed events for the given stream. */
    List<FailedEvent> findByStreamName(String streamName);
}
public record FailedEvent(
    String id,
    String streamName,
    String operationType,
    BsonValue documentKey,
    Document fullDocument,
    BsonDocument resumeToken,
    ErrorInfo error,
    int attempts,
    String status,
    Instant firstAttemptAt,
    Instant lastAttemptAt,
    Instant createdAt,
    Instant expiresAt,
    Map<String, Object> metadata
) {
    public static final String STATUS_PENDING = "PENDING";

    public record ErrorInfo(
        String type,
        String message,
        String stackTrace
    ) {}
}

Roadmap

The following attributes are planned but not yet implemented:
AttributeDescriptionStatus
reprocessStrategyAutomatic reprocessing strategy (MANUAL, AUTO_CRON)Planned
reprocessCronCron expression for automatic reprocessingPlanned
reprocessBatchSizeBatch size for automatic reprocessingPlanned
mongoTemplateRefCustom MongoTemplate bean reference for multi-datasource setupsPlanned

See Also

Retry & DLQ Guide

Understand DLQ routing, manual sends, document schema, and best practices

@RetryPolicy

Configure exponential backoff retry for failed handlers

@Checkpoint

Resume token persistence for reliable stream recovery

ChangeStreamContext

Runtime context including sendToDlq(), attempt number, and more