bullmq
>=5.0.0 <6.0.0postconditions23functions21last verified2026-06-23coverage score100%Postconditions: what we check
- add · queue-add-redis-errorerrorWhenRedis connection fails or is down during add operationThrows
Error with Redis connection failure detailsRequired handlingCaller MUST wrap Queue.add() in try-catch to handle Redis connection errors. Redis downtime crashes application if unhandled. Use try-catch to gracefully degrade or retry.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - addBulk · queue-addbulk-redis-errorerrorWhenRedis connection fails during bulk operationThrows
Error causing partial or complete bulk operation failureRequired handlingCaller MUST wrap Queue.addBulk() in try-catch to handle Redis errors. Bulk operations can partially fail if Redis connection is lost - must handle to prevent data loss.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - process · worker-process-job-errorwarningWhenjob processing throws error or promise rejectsThrows
Error causing job to fail and trigger retry logic if configuredRequired handlingWorker processor SHOULD handle job errors gracefully with try-catch to control retry behavior and provide meaningful error messages. Unhandled errors in processor can cause worker to stop processing subsequent jobs.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - close · close-errorwarningWhenconnection close operation fails (timeout, Redis error)Throws
Error during connection cleanupRequired handlingCaller SHOULD handle close() errors during graceful shutdown to ensure proper cleanup. Use try-catch in shutdown handlers to log but not crash on close errors.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[3] - run · worker-run-no-error-listenererrorWhenWorker started without 'error' event listener attachedThrows
Unhandled Error event from EventEmitter when Redis errors occurRequired handlingCaller MUST attach worker.on('error', handler) BEFORE calling worker.run(). Without an error listener, Node.js throws an unhandled error event that crashes the process. The BullMQ docs explicitly warn: 'If the error handler is missing, your worker may stop processing jobs when an error is emitted.'costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[2] - run · worker-run-redis-connection-errorerrorWhenRedis connection fails during worker main loopThrows
Error emitted via 'error' event on worker instanceRequired handlingCaller MUST wrap Worker usage with error event listeners. Redis connection loss during run() causes the worker to emit 'error' events. Without maxRetriesPerRequest set to null for consumers, the worker will throw after retry limit is reached.costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - FlowProducer.add · flow-add-redis-errorerrorWhenRedis connection fails during atomic flow additionThrows
Error with Redis connection failure detailsRequired handlingCaller MUST wrap FlowProducer.add() in try-catch. Flow additions are atomic (all-or-nothing) via Redis transactions, but Redis connection failure during the transaction causes the entire flow to fail. Without error handling, background pipeline orchestration silently stops.costhighin prodimmediate exceptionusers seedegraded performancevisibilitysilentSources[5] - FlowProducer.addBulk · flow-addbulk-redis-errorerrorWhenRedis connection fails during bulk flow additionThrows
Error with Redis connection failure detailsRequired handlingCaller MUST wrap FlowProducer.addBulk() in try-catch. Bulk flow additions are atomic — Redis failure loses all flows in the batch. Must handle to prevent silent pipeline breakage.costhighin prodimmediate exceptionusers seedegraded performancevisibilitysilentSources[5] - upsertJobScheduler · scheduler-upsert-redis-errorerrorWhenRedis connection fails during scheduler upsertThrows
Error with Redis connection failure detailsRequired handlingCaller MUST wrap upsertJobScheduler() in try-catch. Scheduler creation failure means recurring jobs silently stop being produced. This is especially dangerous during deployment — if scheduler registration fails, no cron jobs run until the next successful registration.costhighin prodsilent failureusers seedegraded performancevisibilitysilentSources[6] - pause · queue-pause-redis-errorwarningWhenRedis connection fails during pause operationThrows
Error with Redis connection failure detailsRequired handlingCaller SHOULD wrap pause() in try-catch. Pause failure during maintenance windows means jobs continue processing when they should be stopped. Handle errors to verify pause actually took effect.costmediumin prodsilent failureusers seedegraded performancevisibilitysilentSources[1] - resume · queue-resume-redis-errorwarningWhenRedis connection fails during resume operationThrows
Error with Redis connection failure detailsRequired handlingCaller SHOULD wrap resume() in try-catch. Resume failure means the queue stays paused — jobs accumulate in waiting state without being processed. Must verify resume succeeded.costmediumin prodsilent failureusers seedegraded performancevisibilitysilentSources[1] - obliterate · queue-obliterate-active-jobs-errorerrorWhenQueue has active jobs and force option is not setThrows
Error indicating queue cannot be obliterated with active jobsRequired handlingCaller MUST wrap obliterate() in try-catch. The method throws if there are active jobs and force=false. Additionally, this is an iterative SCAN-based operation — large queues may fail mid-obliteration on Redis errors, leaving the queue in a partially destroyed state.costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - waitUntilFinished · job-wait-until-finished-timeouterrorWhenJob does not complete within the specified TTLThrows
Error indicating job timed outRequired handlingCaller MUST wrap waitUntilFinished() in try-catch. The promise rejects on both job failure AND timeout. Using waitUntilFinished() in request-response patterns (e.g., HTTP handler waits for job result) without timeout handling causes hung requests. Always set a TTL parameter.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[5] - waitUntilFinished · job-wait-until-finished-job-failederrorWhenThe job fails during processingThrows
Error with the job's failedReasonRequired handlingCaller MUST handle rejection from waitUntilFinished(). When the job's processor throws, this promise rejects with the failure reason. Without try-catch, the calling code gets an unhandled rejection.costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[5] - Job.remove · job-remove-locked-errorerrorWhenJob is currently being processed by a worker (locked)Throws
Error indicating job is locked and cannot be removedRequired handlingCaller MUST wrap job.remove() in try-catch. Active jobs have locks and cannot be removed — the method throws rather than silently failing. Handle this to implement retry-after-completion logic.costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[7] - Job.retry · job-retry-invalid-stateerrorWhenJob is not in failed or completed state, or job does not existThrows
Error indicating job cannot be retried (wrong state, locked, or missing)Required handlingCaller MUST wrap job.retry() in try-catch. The method throws if the job is locked, does not exist, or is not in a finished state. Handle errors to avoid crashing retry-management code.costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisibleSources[8] - clean · queue-clean-redis-errorwarningWhenRedis connection fails during clean operationThrows
Error with Redis connection failure detailsRequired handlingCaller SHOULD wrap clean() in try-catch. Clean operations run Lua scripts against Redis — connection failures leave the queue in an inconsistent state. Handle errors in maintenance/cron code to prevent silent cleanup failures.costlowin prodsilent failureusers seedegraded performancevisibilitysilentSources[1] - drain · queue-drain-redis-errorwarningWhenRedis connection fails during drain operationThrows
Error with Redis connection failure detailsRequired handlingCaller SHOULD wrap drain() in try-catch. Drain failure during deployment/maintenance means stale jobs remain in queue. Handle to verify drain completed.costlowin prodsilent failureusers seedegraded performancevisibilitysilentSources[1] - retryJobs · queue-retryjobs-redis-errorwarningWhenRedis connection fails during batch retry operationThrows
Error with Redis connection failure detailsRequired handlingCaller SHOULD wrap retryJobs() in try-catch. Batch retry operations iterate over potentially large job sets — Redis failure mid-iteration causes partial retries. Handle to track which jobs were actually retried.costmediumin prodsilent failureusers seedegraded performancevisibilitysilentSources[8] - promoteJobs · queue-promotejobs-redis-errorwarningWhenRedis connection fails during batch promote operationThrows
Error with Redis connection failure detailsRequired handlingCaller SHOULD wrap promoteJobs() in try-catch. Like retryJobs(), this iterates over potentially large delayed-job sets via Redis Lua scripts. Connection loss mid-iteration leaves jobs partially promoted — some in waiting, others still delayed — with no way for the caller to know how many transitioned. Handle to track and resume promotion.costmediumin prodsilent failureusers seedegraded performancevisibilitysilentSources[9] - removeOrphanedJobs · queue-removeorphanedjobs-redis-errorerrorWhenRedis connection fails during SCAN-based iterationThrows
Error with Redis connection failure detailsRequired handlingCaller MUST wrap removeOrphanedJobs() in try-catch. The method runs an iterative SCAN+Lua loop over the entire queue keyspace and a connection failure mid-loop leaves orphans behind without returning a count. The d.ts comment recommends calling it in a while-loop with a limit to bound iteration; combine with try-catch so the loop can resume from cursor 0 after a transient Redis failure rather than crashing the migration script.costmediumin prodsilent failureusers seedegraded performancevisibilitysilentSources[10] - rateLimit · queue-ratelimit-redis-errorwarningWhenRedis connection fails during limiter SET operationThrows
Error with Redis connection failure detailsRequired handlingCaller SHOULD wrap rateLimit() in try-catch. The method writes a single Redis SET on the limiter key — failure means the runtime throttle was never applied. In flows where rateLimit() is called in response to an upstream 429 (e.g. third-party API rate limit), a swallowed Redis error leads to continued requests against the upstream and an extended rate-limit ban.costmediumin prodsilent failureusers seedegraded performancevisibilitysilentSources[11] - setGlobalConcurrency · queue-setglobalconcurrency-redis-errorwarningWhenRedis connection fails during HSET on the meta hashThrows
Error with Redis connection failure detailsRequired handlingCaller SHOULD wrap setGlobalConcurrency() in try-catch. Failure means the global concurrency limit was never persisted — workers continue at the prior limit (or unbounded if it was never set). Common deployment failure: a startup hook calls setGlobalConcurrency() to clamp workers during a downstream incident; a swallowed Redis error means the clamp never takes effect and the downstream gets overrun.costmediumin prodsilent failureusers seedegraded performancevisibilitysilentSources[12]
Sources
Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.
- [1]docs.bullmq.io/guide/queuesQueues
- [2]docs.bullmq.io/guide/workersWorkers
- [3]docs.bullmq.io/guide/connectionsConnections
- [4]docs.bullmq.io/patterns/failing-fast-when-redis-is-downFailing Fast When Redis Is Down
- [5]docs.bullmq.io/guide/flowsFlows
- [6]docs.bullmq.io/guide/job-schedulersJob Schedulers
- [7]docs.bullmq.io/guide/jobsJobs
- [8]docs.bullmq.io/guide/retrying-failing-jobsRetrying Failing Jobs
- [9]api.docs.bullmq.io/classes/v5.Queue.htmlV5.Queue
- [10]api.docs.bullmq.io/classes/v5.Queue.htmlV5.Queue
- [11]api.docs.bullmq.io/classes/v5.Queue.htmlV5.Queue
- [12]api.docs.bullmq.io/classes/v5.Queue.htmlV5.Queue
Research notes
Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.
Sources: bullmq
Package: bullmq
Version: 5.70.1
Category: queue (Redis-based job queue and message queue)
Official Documentation
Homepage: https://bullmq.io/ Repository: https://github.com/taskforcesh/bullmq npm: https://www.npmjs.com/package/bullmq
Key Documentation Sections
- Quick Start: https://docs.bullmq.io/readme-1
- Queues: https://docs.bullmq.io/guide/queues
- Workers: https://docs.bullmq.io/guide/workers
- Connections: https://docs.bullmq.io/guide/connections
- Retrying Failing Jobs: https://docs.bullmq.io/guide/retrying-failing-jobs
- Failing Fast When Redis is Down: https://docs.bullmq.io/patterns/failing-fast-when-redis-is-down
- Going to Production: https://docs.bullmq.io/guide/going-to-production
Behavioral Requirements
Error Handling
Redis Connection Errors:
- Queue.add() can fail if Redis is down or connection is lost
- Must wrap Queue operations in try-catch blocks
- Use
enableOfflineQueue: falseto fail fast instead of queuing commands - Set
maxRetriesPerRequest: 1for faster failure detection
Job Processing Errors:
- Worker automatically moves jobs to "failed" status if processor throws
- Unhandled errors can cause worker to stop processing jobs
- Must handle errors in worker processor to keep queue running
- Default retry: 3 times, then moves to dead letter queue
Error Events:
- Attach handlers for
errorevent on Queue and Worker instances - Listen to
failedevent withworker.on('failed', (job, err) => {...})
Sources:
Resource Management
Connection Cleanup:
- Every BullMQ class consumes at least one Redis connection
- Must call
.close()on Queue and Worker instances to release connections - Connection leaks can exhaust Redis max clients limit
- Cleanup is especially important in HTTP endpoints that create queues
Connection Reuse:
- Queue and Worker can accept existing ioredis instances
- QueueScheduler and QueueEvents require blocking connections (cannot reuse)
- Shared connections improve resource utilization
Sources:
Common Mistakes
1. Not Handling Redis Disconnections:
- Assuming Redis is always available
- Solution: Wrap Queue.add() in try-catch, handle connection errors
2. Ignoring Worker Error Events:
- Worker stops processing when error event is not handled
- Solution: Always attach error event handlers
3. Not Closing Connections:
- Creating Queue instances in HTTP endpoints without cleanup
- Solution: Reuse Queue instances or always call .close()
4. Improper Error Handling in Processors:
- Try-catch in processor prevents automatic retry mechanism
- Solution: Let errors throw naturally unless you need custom handling
5. Missing Retry Configuration:
- Not configuring retry attempts for critical jobs
- Solution: Set retry options when adding jobs
Sources:
CVE Analysis
No known behavioral CVEs as of 2026-02-25.
Checked: CVE Database
BullMQ is actively maintained with regular security updates. Primary risks are:
- Operational: Redis connection failures
- Misconfiguration: Improper error handling leading to queue blocking
Real-World Examples
Real-world examples of proper usage:
- GitHub Code Search: bullmq examples
- Official Examples: BullMQ Examples Repository
- Medium Articles: Message Queue in Node.js with BullMQ and Redis
- Production Patterns: How to Process Scheduled Queue Jobs
Contract Rationale
Why these contracts matter:
Problem Prevention
-
Application Crashes from Redis Failures:
- Without error handling, Redis connection failures crash the entire application
- Jobs queued via HTTP endpoints need immediate failure feedback
- Contract ensures graceful degradation when Redis is unavailable
-
Queue Blocking from Worker Errors:
- Unhandled errors in worker processors can stop the entire queue
- Other jobs wait indefinitely while worker is stuck
- Contract ensures workers handle errors and continue processing
-
Connection Leaks:
- Creating Queue/Worker instances in HTTP endpoints without cleanup
- Exhausts Redis connection pool over time
- Contract enforces proper connection management
Consequences of Violations
Severity: ERROR (Redis Connection Failures)
- Impact: Immediate application crash
- Frequency: Common in production (network issues, Redis restarts)
- Cost: Service outages, lost user requests
- Example: HTTP endpoint creating queue without try-catch → 500 error cascade
Severity: WARNING (Worker Error Handling)
- Impact: Queue stops processing, jobs pile up
- Frequency: Moderate (depends on job complexity)
- Cost: Delayed processing, SLA violations
- Example: Email worker crashes → thousands of undelivered emails
Severity: WARNING (Connection Cleanup)
- Impact: Gradual resource exhaustion
- Frequency: High in microservices with many queue instances
- Cost: Redis connection limit reached, new connections fail
- Example: 1000 requests/minute × unclosed connections → Redis maxed out in 10 minutes
Why Critical for Production
BullMQ is typically used for:
- Critical background jobs (payments, emails, notifications)
- High-throughput systems (thousands of jobs per minute)
- Distributed systems (multiple workers, multiple queues)
- User-facing features (job status tracking, real-time updates)
Production characteristics:
- Redis failures are inevitable (network issues, maintenance, scaling)
- Worker errors will happen (external API failures, data issues)
- Connection management at scale (100+ instances in microservices)
Without these contracts:
- ❌ Application becomes fragile to Redis issues
- ❌ Queue processing can halt unexpectedly
- ❌ Resource leaks degrade system over time
- ❌ Recovery requires manual intervention
With these contracts:
- ✅ Graceful degradation during Redis failures
- ✅ Workers continue processing despite individual job errors
- ✅ Clean resource management at scale
- ✅ Self-healing system design
Created: 2026-02-25 Status: ✅ COMPLETE - Ready for production use