Cancelling jobs
The job cancellation feature allows you to gracefully cancel jobs that are currently being processed by a worker. This is implemented using the standard AbortController and AbortSignal APIs.
How It Works
When a worker processes a job, it can receive an optional AbortSignal as the third parameter in the processor function. This signal can be used to detect when a job has been cancelled and perform cleanup operations.
import { Worker } from 'bullmq';
const worker = new Worker('myQueue', async (job, token, signal) => {
// The signal parameter is optional and provides cancellation support
// Your job processing logic here
});Cancelling Jobs
The Worker class provides methods to cancel jobs:
// Cancel a specific job by ID
const cancelled = worker.cancelJob('job-id-123');
console.log('Job cancelled:', cancelled); // true if job was active, false otherwise
// Cancel with a reason (useful for debugging)
worker.cancelJob('job-id-456', 'User requested cancellation');
// Cancel all active jobs
worker.cancelAllJobs();
// Cancel all with a reason
worker.cancelAllJobs('System shutdown');
// Get list of active jobs from queue
const activeJobs = await queue.getActive();
console.log(
'Active jobs:',
activeJobs.map(j => j.id),
);Cancellation Reasons
When you provide a cancellation reason, it's passed to the AbortController.abort(reason) method and can be accessed via signal.reason:
Handling Cancellation (Recommended Pattern)
The event-based approach is the recommended pattern as it provides immediate response to cancellation:
Why Event-Based?
✅ Immediate response - No polling delay
✅ More efficient - No CPU wasted checking in loops
✅ Cleaner code - Separation of concerns
✅ Standard pattern - Matches Web APIs like
fetch()
Using with Native APIs (Recommended)
Many Web APIs natively support AbortSignal. The signal is composable - you can pass it to APIs and still listen to it yourself:
Why this pattern is better:
✅ Simpler - One abort listener handles everything
✅ Composable - Signal passed to
fetch()AND listened to in job✅ The HTTP request is truly cancelled at the network level
✅ The job is properly marked as failed when cancelled
✅ No complex error checking needed
APIs That Support AbortSignal
Many modern APIs accept signal directly:
fetch(url, { signal })- HTTP requestsaddEventListener(event, handler, { signal })- Auto-removes listener on abortMany database clients (Postgres, MongoDB drivers)
File system operations in newer Node.js APIs
Cancelling Custom Operations
For operations that don't natively support AbortSignal, implement proper cleanup:
Async Cleanup on Cancellation
Perform cleanup operations before rejecting the promise:
Alternative: Polling Pattern
You can also check signal.aborted periodically (less efficient but simpler for some use cases):
Job State After Cancellation
With Regular Error (Will Retry)
When you throw a regular Error upon cancellation:
Job state: Moves to
failedRetries: Job WILL be retried if
attemptsremainUse case: When you want the job to be retried later
With UnrecoverableError (No Retry)
When you throw an UnrecoverableError:
Job state: Moves to
failedRetries: Job will NOT be retried
Use case: When cancellation should be permanent
Handling Lock Renewal Failures
When a worker loses its lock on a job (due to network issues, Redis problems, or long-running operations), you can gracefully handle this situation using the lockRenewalFailed event:
Important: When a worker loses the lock on a job, it cannot move that job to the failed state (as it no longer owns the lock). Instead:
The
cancelJob()aborts the signal, allowing the processor to clean up resourcesThe job remains in
activestate temporarilyBullMQ's stalled job checker will detect the job and move it back to
waitingAnother worker (or the same worker) will pick it up and retry
This is the correct and intended behavior - trust BullMQ's stalled job mechanism to handle lost locks.
Why This Pattern Works
✅ Immediate cleanup: The processor detects
signal.abortedand can release resources✅ No wasted work: The processor stops processing when it loses the lock
✅ Automatic recovery: The stalled job checker moves the job back to waiting
✅ No data loss: The job will be retried according to its
attemptssetting✅ Works with existing infrastructure: Uses BullMQ's built-in stalled job handling
Multi-Phase Work with Cancellation
Check cancellation at strategic points in multi-phase operations:
Backward Compatibility
The signal parameter is optional. Existing processors that don't use it will continue to work normally:
Best Practices
Use event-based cancellation for immediate response
Clean up resources in the abort handler
Use UnrecoverableError when cancellation should be permanent
Combine with timeouts for better control
Check
signal.abortedat strategic points in long operationsHandle cleanup errors gracefully to avoid leaving resources open
Read more:
Last updated
Was this helpful?