> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowwarden.io/llms.txt
> Use this file to discover all available pages before exploring further.

# @DeadLetterQueue

> Automatically capture failed events for later investigation and reprocessing

The `@DeadLetterQueue` annotation enables **automatic routing of failed events** to a Dead Letter Queue backend. 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.

The annotation itself is **backend-agnostic**: it carries only the cross-cutting policy (retention, payload flags) that every DLQ backend can honour. Backend-specific tuning (e.g. the MongoDB collection name) lives on a companion annotation such as [`@MongoDlqOptions`](#mongo-specific-options-mongodlqoptions).

## Attributes

| Attribute                 | Type      | Default | Description                                               |
| ------------------------- | --------- | ------- | --------------------------------------------------------- |
| `enabled`                 | `boolean` | `true`  | Whether DLQ is enabled                                    |
| `retentionDays`           | `int`     | `30`    | Retention in days. `0` means permanent (no expiry)        |
| `includeOriginalDocument` | `boolean` | `true`  | Whether to include the original document in the DLQ entry |
| `includeStackTrace`       | `boolean` | `true`  | Whether to include the full stack trace in the DLQ entry  |

### `retentionDays`

How long DLQ entries are retained before automatic cleanup. Each backend translates this to its native mechanism — the MongoDB backend sets a TTL index on the `expiresAt` field; Kafka maps it to `retention.ms`; RabbitMQ to the `x-message-ttl` header; JDBC to a scheduled cleanup job. Set to `0` to keep entries permanently.

```java theme={null}
// Keep failed events for 90 days
@DeadLetterQueue(retentionDays = 90)

// Keep failed events permanently (no auto-cleanup)
@DeadLetterQueue(retentionDays = 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.

```java theme={null}
// Minimal DLQ entry — only metadata, no document or stack trace
@DeadLetterQueue(includeOriginalDocument = false, includeStackTrace = false)
```

## Mongo-specific options: `@MongoDlqOptions`

`@DeadLetterQueue` deliberately does not carry the MongoDB collection name. Routing a stream's failed events to a custom collection is a backend-specific concern, declared on the companion annotation `@MongoDlqOptions`:

```java theme={null}
@ChangeStream(collection = "orders")
@DeadLetterQueue(retentionDays = 90)
@MongoDlqOptions(collection = "orders_dlq")
public class OrderHandler { ... }
```

| Attribute    | Type     | Default | Description                                                                           |
| ------------ | -------- | ------- | ------------------------------------------------------------------------------------- |
| `collection` | `String` | `""`    | MongoDB collection for this stream's DLQ entries. Empty means use the global default. |

The global default for the collection name is configured by the [`flowwarden.dlq.mongo.collection`](/reference/configuration#flowwarden-dlq-mongo) property (defaults to `_fw_dlq`). Streams without `@MongoDlqOptions` write to that collection.

<Note>
  `@MongoDlqOptions` has no effect when the configured `DlqStore` implementation is non-Mongo. Each backend ships its own companion annotation.
</Note>

The MongoDB TTL index on `expiresAt` is created automatically at startup for every collection bound to a `@DeadLetterQueue`-annotated stream, so `retentionDays` takes effect without any manual index setup.

## Storage

Failed events are persisted via the [`DlqStore` SPI](/reference/dlq-store). The MongoDB-backed implementation (`MongoDlqStore`) is auto-configured by default and creates the `expiresAt` TTL index automatically at startup. Custom backends (Kafka, RabbitMQ, JDBC, …) plug in by declaring a `@Bean DlqStore`. The [Redis satellite](/redis) does **not** ship a `DlqStore` — Redis-backed deployments either keep the Mongo DLQ or supply a custom bean.

## Roadmap

The following attributes are planned but not yet implemented:

| Attribute            | Description                                                                               | Status  |
| -------------------- | ----------------------------------------------------------------------------------------- | ------- |
| `reprocessStrategy`  | Automatic reprocessing strategy (`MANUAL`, `AUTO_CRON`)                                   | Planned |
| `reprocessCron`      | Cron expression for automatic reprocessing                                                | Planned |
| `reprocessBatchSize` | Batch size for automatic reprocessing                                                     | Planned |
| `mongoTemplateRef`   | Custom `MongoTemplate` bean reference for multi-datasource setups (on `@MongoDlqOptions`) | Planned |

## See Also

<CardGroup cols={2}>
  <Card title="Retry & DLQ Guide" icon="map" href="/guides/retry-and-dlq">
    Understand DLQ routing, manual sends, document schema, and best practices
  </Card>

  <Card title="@RetryPolicy" icon="rotate-right" href="/reference/retry-policy">
    Configure exponential backoff retry for failed handlers
  </Card>

  <Card title="@Checkpoint" icon="bookmark" href="/reference/checkpoint">
    Resume token persistence for reliable stream recovery
  </Card>

  <Card title="ChangeStreamContext" icon="circle-info" href="/reference/change-stream-context">
    Runtime context including sendToDlq(), attempt number, and more
  </Card>
</CardGroup>
