All jobs in BullMQ need a unique job ID. This ID is used to construct a key for storing data in Redis and serves as a pointer to the job as it moves between different states during its lifetime.
The uniqueness requirement is scoped by queue. You can have the same job ID in different queues without issues. The counter for automatically generated IDs is also scoped by queue.
By default, job IDs are generated automatically as an increasing counter:
import { Queue } from 'bullmq';const myQueue = new Queue('Paint');// Job will get an auto-generated ID like "1", "2", "3", etc.const job = await myQueue.add('wall', { color: 'pink' });console.log(job.id); // "1"
The main reason to specify a custom ID is to avoid duplicated jobs. Since IDs must be unique, adding a job with an existing ID will be ignored and not added to the queue.
Jobs removed from the queue (either manually or via removeOnComplete/removeOnFail) will not be considered duplicates. You can add the same job ID many times as long as the previous job has been removed.
if (this.opts?.jobId) { // Custom Id cannot be integers if (`${parseInt(this.opts.jobId, 10)}` === this.opts?.jobId) { throw new Error('Custom Id cannot be integers'); } // Custom Id cannot contain ':' if ( this.opts?.jobId.includes(':') && this.opts?.jobId?.split(':').length !== 3 ) { throw new Error('Custom Id cannot contain :'); }}
Custom job IDs must not:
Be pure integers (e.g., “123” is invalid, but “job-123” is valid)
Contain the : separator (as it conflicts with Redis naming conventions)
When you attempt to add a job with an existing ID, BullMQ will:
Check if the ID already exists
Ignore the new job
Emit a duplicated event via QueueEvents
import { QueueEvents } from 'bullmq';const queueEvents = new QueueEvents('Paint');queueEvents.on('duplicated', ({ jobId }) => { console.log(`Job ${jobId} was not added because it already exists`);});// First job is addedawait myQueue.add('wall', { color: 'pink' }, { jobId: 'paint-1' });// Second job is ignored and 'duplicated' event is emittedawait myQueue.add('wall', { color: 'blue' }, { jobId: 'paint-1' });
interface BaseJobOptions { /** * Override the job ID - by default, the job ID is a unique * integer, but you can use this setting to override it. * If you use this option, it is up to you to ensure the * jobId is unique. If you attempt to add a job with an id that * already exists, it will not be added. */ jobId?: string;}