Retries and failures
Retries are part of the Job product. A task handler can throw when work fails. Job records the failed attempt, chooses the next delay, and asks Queue to deliver another attempt until the run succeeds or exhausts attempts.
Instance defaults
Section titled “Instance defaults”Configure defaults on the Job instance:
const jobs = job({ name: "background", retry: { attempts: 3, backoff: "exponential", initialDelaySeconds: 5, maxDelaySeconds: 300, },})Every task inherits this policy.
Task overrides
Section titled “Task overrides”Override retry policy for a task:
jobs.task("send-email", { retry: { attempts: 5, backoff: "fixed", initialDelaySeconds: 30, },}, async (payload) => { await sendEmail(payload)})Custom retry delays
Section titled “Custom retry delays”Webhook delivery often needs specific retry intervals. Use custom delays when attempt spacing should follow an explicit sequence:
jobs.task("deliver-webhook", { retry: { attempts: 8, backoff: { type: "custom", delaysSeconds: [ 10, 30, 120, 300, 900, 3600, 21600, ], }, },}, async (payload) => { await deliverWebhook(payload)})The first failed attempt uses the first delay. When all attempts are exhausted,
the run becomes dead_lettered.
Failure behavior
Section titled “Failure behavior”attempt starts -> handler throws -> attempt is marked failed -> run is marked retrying -> Queue retries after the selected delayWhen the final attempt fails:
attempt starts -> handler throws -> attempt is marked failed -> run is marked dead_lettered -> message is acknowledgedDead-lettered runs remain queryable:
const failed = await jobs.runs.list({ status: "dead_lettered", taskName: "deliver-webhook",})Replay
Section titled “Replay”Replay creates a new run with the same task and payload:
await jobs.runs.replay(runId, { reason: "endpoint recovered",})Use replay after fixing a handler, restoring a provider, or updating the destination endpoint.
Cancellation
Section titled “Cancellation”await jobs.runs.cancel(runId)Cancellation marks the run as canceled. A Queue delivery that later wakes up
for a canceled run is acknowledged without executing the task handler.
Handler design
Section titled “Handler design”Task handlers should tolerate retries. Use stable business IDs and idempotency keys when the task performs side effects:
jobs.task("charge-card", {}, async (payload) => { await payments.charge({ customerId: payload.customerId, amount: payload.amount, idempotencyKey: payload.paymentAttemptId, })})