Retrying Jobs
BullMQ provides a retry method that allows you to programmatically retry jobs that have already completed or failed. This is different from the automatic retry mechanism (configured via the attempts option) - the retry method lets you manually move a job back to the waiting queue at any time.
When to use Job.retry()
The retry method is useful in scenarios such as:
- Manual intervention: When a job failed due to a temporary external issue that has been resolved
- Re-processing completed jobs: When you need to run a completed job again with the same data
- Workflow recovery: When recovering from system failures or bugs that caused jobs to fail incorrectly
INFO
Only jobs in the completed or failed state can be retried. Active, waiting, or delayed jobs cannot be retried.
Basic Usage
import { Queue, Job } from 'bullmq';
const queue = new Queue('my-queue');
// Get a failed job by ID
const job = await Job.fromId(queue, 'job-id');
// Retry a failed job (default state is 'failed')
await job.retry();
// Retry a completed job
await job.retry('completed');from bullmq import Queue, Job
queue = Queue('my-queue')
# Get a failed job by ID
job = await Job.fromId(queue, 'job-id')
# Retry a failed job (default state is 'failed')
await job.retry()
# Retry a completed job
await job.retry('completed')
await queue.close()# Get a job reference (must have connection set)
job = %Job{id: "job-id", queue_name: "my-queue", prefix: "bull", connection: conn}
# Retry a failed job (default state is :failed)
{:ok, updated_job} = Job.retry(job)
# Retry a completed job
{:ok, updated_job} = Job.retry(job, :completed)use bullmq::{Queue, QueueOptions, Job};
use bullmq::types::RetryOptions;
let queue = Queue::new("my-queue", QueueOptions::default()).await?;
// Get a failed job by ID
let mut job = queue.get_job("job-id").await?.unwrap();
// Retry a failed job (default state is "failed")
job.retry("failed", None).await?;
// Retry a completed job
job.retry("completed", None).await?;Retry Options
The retry method accepts options to reset attempt counters. This is useful when you want the retried job to behave as if it's being processed for the first time.
Reset Attempts Made
The attemptsMade counter tracks how many times a job has been processed. Resetting it allows the job to use its full retry allowance again.
// Retry and reset the attempts counter
await job.retry('failed', { resetAttemptsMade: true });# Retry and reset the attempts counter
await job.retry('failed', {"resetAttemptsMade": True})# Retry and reset the attempts counter
{:ok, updated_job} = Job.retry(job, :failed, reset_attempts_made: true)use bullmq::types::RetryOptions;
// Retry and reset the attempts counter
job.retry("failed", Some(RetryOptions {
reset_attempts_made: true,
..Default::default()
})).await?;Reset Attempts Started
The attemptsStarted counter tracks how many times a job has been moved to the active state. This can be useful for tracking purposes.
// Retry and reset both counters
await job.retry('failed', {
resetAttemptsMade: true,
resetAttemptsStarted: true,
});# Retry and reset both counters
await job.retry('failed', {
"resetAttemptsMade": True,
"resetAttemptsStarted": True
})# Retry and reset both counters
{:ok, updated_job} = Job.retry(job, :failed,
reset_attempts_made: true,
reset_attempts_started: true
)use bullmq::types::RetryOptions;
// Retry and reset both counters
job.retry("failed", Some(RetryOptions {
reset_attempts_made: true,
reset_attempts_started: true,
})).await?;What happens when you retry
When a job is retried, the following occurs:
- Job is moved to waiting queue: The job is removed from the completed/failed set and added back to the waiting queue
- Properties are cleared: The following job properties are reset:
failedReason/failed_reason→null/nilfinishedOn/finished_on→null/nilprocessedOn/processed_on→null/nilreturnvalue/return_value→null/nil
- Events are emitted: A
waitingevent is emitted when the job is successfully moved - Parent dependencies restored: If the job is a child in a flow, its dependency relationship with the parent is restored
WARNING
If you retry a job without resetting attemptsMade, and the job has already exhausted its retry attempts, it will fail immediately when processed again.
Error Handling
The retry method can fail in the following cases:
| Error Code | Description |
|---|---|
-1 | Job does not exist |
-3 | Job was not found in the expected state |
try {
await job.retry('failed');
} catch (error) {
console.error('Failed to retry job:', error.message);
}try:
await job.retry('failed')
except Exception as error:
print(f'Failed to retry job: {error}')case Job.retry(job, :failed) do
{:ok, updated_job} ->
IO.puts("Job retried successfully")
{:error, {:reprocess_failed, -1}} ->
IO.puts("Job does not exist")
{:error, {:reprocess_failed, -3}} ->
IO.puts("Job was not in the expected state")
{:error, reason} ->
IO.puts("Failed to retry: #{inspect(reason)}")
endmatch job.retry("failed", None).await {
Ok(()) => println!("Job retried successfully"),
Err(err) => eprintln!("Failed to retry job: {err}"),
}Read More
- 💡 Retry API Reference
- 💡 Retrying Failing Jobs - Automatic retry configuration with backoff strategies
- 💡 Stop Retrying Jobs - How to prevent further retries