Read it, retry it, clear it.
The four things you actually do with a Dead Letter Queue: narrow it down to the entries you care about, put a subset back, check whether the pile is growing, and empty it when the cause is fixed.
Common Tasks
Section titled “Common Tasks”Filter entries
Section titled “Filter entries”await queue.getDlqAsync({ reason: 'max_attempts_exceeded' }); // by emitted reasonawait queue.getDlqAsync({ olderThan: Date.now() - 86400000 }); // older than 24hawait queue.getDlqAsync({ newerThan: Date.now() - 3600000 }); // last hourawait queue.getDlqAsync({ retriable: true }); // auto-retry is due nowawait queue.getDlqAsync({ limit: 10, offset: 20 }); // paginationThe SDK’s getDlq() returns jobs only. Use the HTTP endpoint for full entry
metadata, then filter the returned page in the client:
const response = await fetch('http://localhost:6790/queues/emails/dlq?limit=100&offset=0');const { entries, total } = await response.json();const stalled = entries.filter((entry) => entry.reason === 'stalled');The SDK’s get_dlq() returns jobs only. The standard library can read full
entries from HTTP:
import jsonfrom urllib.request import urlopen
with urlopen("http://localhost:6790/queues/emails/dlq?limit=100&offset=0") as response: payload = json.load(response)stalled = [entry for entry in payload["entries"] if entry["reason"] == "stalled"]The SDK’s getDlq() returns jobs only. Read full entries through HTTP:
$json = file_get_contents('http://localhost:6790/queues/emails/dlq?limit=100&offset=0');$payload = json_decode($json, true, flags: JSON_THROW_ON_ERROR);$stalled = array_filter($payload['entries'], fn ($entry) => $entry['reason'] === 'stalled');The SDK’s GetDlq() returns jobs only. Read full entries through HTTP with the
standard library:
response, err := http.Get("http://localhost:6790/queues/emails/dlq?limit=100&offset=0")if err != nil { return err }defer response.Body.Close()
var payload struct { Entries []map[string]any `json:"entries"` }err = json.NewDecoder(response.Body).Decode(&payload)The Rust SDK’s get_dlq() returns jobs only. Use the broker HTTP endpoint (and
your application’s HTTP/JSON crate) for full entries:
curl 'http://localhost:6790/queues/emails/dlq?limit=100&offset=0'The Elixir SDK’s dlq/2 returns jobs only. Use the broker HTTP endpoint for
full entries:
curl 'http://localhost:6790/queues/emails/dlq?limit=100&offset=0'The Bun async API applies reason, time, due-retry, expiry, limit, and offset
filters server-side in both embedded and TCP modes. The HTTP endpoint currently
supports only limit and offset; external clients must filter that page
locally. retriable: true means nextRetryAt is already due, not merely that
the entry has retry budget remaining.
Retry selectively
Section titled “Retry selectively”queue.retryDlq(); // retry everythingqueue.retryDlq('job-123'); // retry one jobqueue.retryDlqByFilter({ reason: 'stalled' }); // TCP is fire-and-forgetconst filtered = await queue.retryDlqByFilterAsync({ reason: 'stalled' }); // authoritative countconst n = await queue.retryDlqAsync(); // retry and get the count (TCP too)await queue.retryDlq(); // retry everythingawait queue.retryDlq('job-123'); // retry one jobawait queue.retryJobs({ count: 100 }); // retry only the first 100 entriesqueue.retry_dlq() # retry everythingqueue.retry_dlq("job-123") # retry one jobqueue.retry_dlq(count=100) # retry only the first 100 entries$queue->retryDlq(); // retry everything$queue->retryDlq('job-123'); // retry one job$queue->retryDlq(null, 100); // retry only the first 100 entriesn, err := queue.RetryDlq("", 0) // retry everythingn, err = queue.RetryDlq("job-123", 0) // retry one jobn, err = queue.RetryDlq("", 100) // retry only the first 100 entriesqueue.retry_dlq(None, None)?; // retry everythingqueue.retry_dlq(Some("job-123"), None)?; // retry one jobqueue.retry_dlq(None, Some(100))?; // retry only the first 100 entries{:ok, n} = Bunqueue.Queue.retry_dlq(queue) # retry everything{:ok, _} = Bunqueue.Queue.retry_dlq(queue, "job-123") # retry one job{:ok, _} = Bunqueue.Queue.retry_dlq(queue, nil, 100) # retry only the first 100Check DLQ health
Section titled “Check DLQ health”const stats = await queue.getDlqStatsAsync();console.log(stats.total); // total entriesconsole.log(stats.byReason); // { max_attempts_exceeded: 5, stalled: 2, ... }console.log(stats.pendingRetry); // entries whose auto-retry time is dueA simple alert loop:
setInterval(async () => { const stats = await queue.getDlqStatsAsync(); if (stats.total > 100) alertOps('High DLQ count', stats);}, 30000);const { stats } = await (await fetch( 'http://localhost:6790/queues/emails/dlq/stats')).json();with urlopen("http://localhost:6790/queues/emails/dlq/stats") as response: stats = json.load(response)["stats"]$payload = json_decode( file_get_contents('http://localhost:6790/queues/emails/dlq/stats'), true, flags: JSON_THROW_ON_ERROR,);$stats = $payload['stats'];response, err := http.Get("http://localhost:6790/queues/emails/dlq/stats")// Decode response.Body and read the top-level "stats" object.curl 'http://localhost:6790/queues/emails/dlq/stats'curl 'http://localhost:6790/queues/emails/dlq/stats'Use synchronous getDlqStats() for a Bun embedded snapshot and
getDlqStatsAsync() for an authoritative Bun result in either runtime. The
other SDKs do not expose a stats helper yet; the HTTP result is authoritative.
const purged = queue.purgeDlq(); // permanently deletes all entriesconst n = await queue.purgeDlqAsync(); // same, but waits and returns the count (TCP too)const purged = await queue.purgeDlq(); // permanently deletes all entries, returns the countpurged = queue.purge_dlq() # permanently deletes all entries, returns the count$purged = $queue->purgeDlq(); // permanently deletes all entries, returns the countpurged, err := queue.PurgeDlq() // permanently deletes all entries, returns the countlet purged = queue.purge_dlq()?; // permanently deletes all entries, returns the count{:ok, purged} = Bunqueue.Queue.purge_dlq(queue) # deletes all entries, returns the countWhere to go next
Section titled “Where to go next”| DLQ Operations from the Queue Object | The same operations from an existing Queue instance |
| Dead Letter Queue | What the DLQ is, and a first look at what failed |
| Automatic DLQ Retry with Backoff | Let bunqueue re-queue dead entries on a backoff |
| DLQ Configuration | autoRetry, maxAge, maxEntries and the defaults |
| DLQ Reference | Failure reasons, entry shape, every DLQ method |