Skip to content

Dead Letter Queue

The dead letter queue (DLQ) captures jobs that have permanently failed, providing a safety net for investigation and manual recovery. Jobs enter the DLQ when retries are exhausted, a handler explicitly discards them, or a non-retryable error occurs.

A job moves to the dead letter queue when:

  1. Retry exhaustion — All retry attempts are consumed and on_exhaustion is "dead_letter".
  2. Handler discard — The handler returns a DEAD_LETTER response code, skipping remaining retries.
  3. Non-retryable error — The error type matches a non_retryable_errors pattern and on_exhaustion is "dead_letter".

Jobs with on_exhaustion: "discard" transition to the discarded state instead and are not placed in the DLQ.

FieldTypeDefaultDescription
max_agestring"P180D"ISO 8601 duration. Jobs older than this are pruned.
max_countinteger10000Maximum number of dead letter jobs. Oldest are pruned first.

When both limits apply, the more restrictive takes effect. Backends MUST enforce at least one retention policy.

Individual dead letter jobs can be retried, returning them to the available state with a fresh attempt counter:

Terminal window
curl -X POST http://localhost:8080/ojs/v1/dead-letter/01961234-5678-7abc/retry

Retrying a dead letter job optionally allows modifications:

{
"retry": {
"max_attempts": 5
},
"priority": 0,
"queue": "high-priority"
}

Multiple dead letter jobs can be retried at once:

Terminal window
curl -X POST http://localhost:8080/ojs/v1/dead-letter/retry \
-H "Content-Type: application/json" \
-d '{"filter": {"job_type": "email.send", "queue": "default"}}'

Backends MAY support automatic replay rules that re-enqueue dead letter jobs matching specific criteria:

{
"filter": {
"job_type": "payment.*",
"error_type": "connection_timeout"
},
"max_replays": 3,
"delay": "PT5M"
}

Replay tracking prevents infinite replay loops. Each replayed job records its replay count, and jobs exceeding max_replays remain in the DLQ.

Dead letter jobs can be filtered by:

FilterDescription
queueOriginal queue name
job_typeJob type (exact or prefix match)
error_typeError type that caused the dead letter
since / untilTime range of entry into DLQ
argsContent-based search within job arguments
Terminal window
# List dead letter jobs for a specific queue
curl "http://localhost:8080/ojs/v1/dead-letter?queue=payments&limit=20"
# Filter by error type
curl "http://localhost:8080/ojs/v1/dead-letter?error_type=connection_timeout"

Backends MUST periodically remove expired dead letter jobs. The pruning strategy options are:

  • Delete — Permanently remove expired jobs (default).
  • Archive — Move expired jobs to cold storage before deletion (for compliance requirements).
MethodPathDescription
GET/ojs/v1/dead-letterList dead letter jobs (paginated)
GET/ojs/v1/dead-letter/{id}Get a specific dead letter job
POST/ojs/v1/dead-letter/{id}/retryRetry a single dead letter job
DELETE/ojs/v1/dead-letter/{id}Delete a dead letter job
POST/ojs/v1/dead-letter/retryBulk retry with filters
DELETE/ojs/v1/dead-letterBulk delete with filters