Retrying jobs
Source URL: https://docs.bullmq.io/guide/jobs/retrying-job
Retrying jobs
Section titled “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()
Section titled “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
{% hint style=“info” %}
Only jobs in the completed or failed state can be retried. Active, waiting, or delayed jobs cannot be retried.
{% endhint %}
Basic Usage
Section titled “Basic Usage”{% tabs %} {% tab title=“TypeScript” %}
import { Queue, Job } from 'bullmq';
const queue = new Queue('my-queue');
// Get a failed job by IDconst job = await Job.fromId(queue, 'job-id');
// Retry a failed job (default state is 'failed')await job.retry();
// Retry a completed jobawait job.retry('completed');{% endtab %}
{% tab title=“Python” %}
from bullmq import Queue, Job
queue = Queue('my-queue')
# Get a failed job by IDjob = await Job.fromId(queue, 'job-id')
# Retry a failed job (default state is 'failed')await job.retry()
# Retry a completed jobawait job.retry('completed')
await queue.close(){% endtab %}
{% tab title=“Elixir” %}
# 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){% endtab %} {% endtabs %}
Retry Options
Section titled “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
Section titled “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.
{% tabs %} {% tab title=“TypeScript” %}
// Retry and reset the attempts counterawait job.retry('failed', { resetAttemptsMade: true });{% endtab %}
{% tab title=“Python” %}
# Retry and reset the attempts counterawait job.retry('failed', {"resetAttemptsMade": True}){% endtab %}
{% tab title=“Elixir” %}
# Retry and reset the attempts counter{:ok, updated_job} = Job.retry(job, :failed, reset_attempts_made: true){% endtab %} {% endtabs %}
Reset Attempts Started
Section titled “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.
{% tabs %} {% tab title=“TypeScript” %}
// Retry and reset both countersawait job.retry('failed', { resetAttemptsMade: true, resetAttemptsStarted: true});{% endtab %}
{% tab title=“Python” %}
# Retry and reset both countersawait job.retry('failed', { "resetAttemptsMade": True, "resetAttemptsStarted": True}){% endtab %}
{% tab title=“Elixir” %}
# Retry and reset both counters{:ok, updated_job} = Job.retry(job, :failed, reset_attempts_made: true, reset_attempts_started: true){% endtab %} {% endtabs %}
What happens when you retry
Section titled “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
{% hint style=“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.
{% endhint %}
Error Handling
Section titled “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 |
{% tabs %} {% tab title=“TypeScript” %}
try { await job.retry('failed');} catch (error) { console.error('Failed to retry job:', error.message);}{% endtab %}
{% tab title=“Python” %}
try: await job.retry('failed')except Exception as error: print(f'Failed to retry job: {error}'){% endtab %}
{% tab title=“Elixir” %}
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)}")end{% endtab %} {% endtabs %}
Read More
Section titled “Read More”- 💡 Retry API Reference
- 💡 Retrying Failing Jobs - Automatic retry configuration with backoff strategies
- 💡 Stop Retrying Jobs - How to prevent further retries