Profiles·Public

bull

semver>=3.0.0 <5.0.0postconditions47functions26last verified2026-06-11coverage score84%

Postconditions — what we check

  • Queue.process · missing-error-handler
    error
    WhenJob processor doesn't handle errors via try-catch or done(error)
    ThrowsThrows unhandled exception that Bull captures and marks job as failed
    Required handlingCaller MUST handle errors in job processor using either: 1. try-catch in async processors: try { await work(); } catch (err) { throw err; } 2. done(error) in callback processors: done(err) Without error handling, exceptions are caught by Bull but may not be logged, and failed jobs accumulate silently if no 'failed' event listener exists.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • Queue.process · missing-failed-listener
    error
    WhenQueue instance created without 'failed' event listener
    ThrowsEmits 'failed' event when job fails
    Required handlingCaller MUST attach 'failed' event listener to queue instance. Without this listener, failed jobs are silently lost with no visibility. CRITICAL: This is production bug #2 (60-70% of codebases). Always add: queue.on('failed', (job, err) => { logger.error('Job failed:', err); })
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[2]
  • Queue.process · missing-stalled-listener
    error
    WhenQueue instance created without 'stalled' event listener
    ThrowsEmits 'stalled' event when job stalls (CPU-intensive code blocks event loop)
    Required handlingCaller MUST attach 'stalled' event listener to queue instance. Stalled jobs are restarted by another worker, resulting in DUPLICATE PROCESSING. CRITICAL: This is production bug #1 (80-90% of codebases don't detect this). Impact: Duplicate emails, duplicate payments, data corruption. Always add: queue.on('stalled', (job) => { logger.error('Job stalled:', job.id); })
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[3]
  • Queue.process · missing-error-listener
    warning
    WhenQueue instance created without 'error' event listener
    ThrowsEmits 'error' event for Redis connection errors, queue errors
    Required handlingCaller SHOULD attach 'error' event listener to queue instance. Without this listener, Redis connection errors and queue errors go unnoticed. Always add: queue.on('error', (error) => { logger.error('Queue error:', error); })
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • new Queue · missing-queue-listeners
    error
    WhenQueue instantiated without required event listeners
    ThrowsQueue emits events but no listeners attached
    Required handlingAfter creating Queue instance, MUST attach event listeners: 1. queue.on('failed', ...) - REQUIRED 2. queue.on('stalled', ...) - CRITICAL 3. queue.on('error', ...) - RECOMMENDED See Queue.process postconditions for details.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • Queue.add · add-redis-connection-error
    error
    WhenQueue.add() called when Redis connection is unavailable
    Required handlingCaller MUST wrap Queue.add() in try-catch and handle the Error. Without error handling, jobs are lost silently with no retry mechanism. Always log the error and implement a fallback (retry queue, dead letter, alert).
    costhighin prodimmediate exceptionusers seedegraded performancevisibilitysilent
    Sources[1][4]
  • Queue.add · add-duplicate-job-id-silent-drop
    warning
    WhenQueue.add() called with a jobId that already exists in the queue
    Required handlingCaller SHOULD verify job creation by checking the returned Job object or querying job status after add(). For deduplication flows, document the intent explicitly. Do not assume add() always creates a new job.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[1]
  • Queue.addBulk · addbulk-redis-connection-error
    error
    WhenQueue.addBulk() called when Redis connection is unavailable
    Required handlingCaller MUST wrap Queue.addBulk() in try-catch and handle the Error. On failure, the entire batch is lost — implement retry logic or store the batch for resubmission. Log the error with the batch contents.
    costhighin prodimmediate exceptionusers seedegraded performancevisibilitysilent
    Sources[1]
  • Queue.addBulk · addbulk-repeat-option-unsupported
    warning
    WhenQueue.addBulk() called with jobs that include a repeat option
    Required handlingDo not use the repeat option with addBulk(). Add repeating jobs individually via Queue.add() to ensure the repeat scheduler is engaged.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[5]
  • Queue.close · close-not-called-on-shutdown
    error
    WhenQueue instance created without registering a SIGTERM/shutdown handler that calls Queue.close()
    Required handlingCaller MUST register process shutdown handlers that call queue.close(). Add: process.on('SIGTERM', () => queue.close()) and: process.on('SIGINT', () => queue.close()) Without this, the process hangs on exit and leaks Redis connections.
    costmediumin proddelayed failureusers seeservice unavailablevisibilityvisible
    Sources[1]
  • Queue.close · close-called-mid-job
    warning
    WhenQueue.close() called while active jobs are still processing
    Required handlingSet a timeout for graceful shutdown. If shutdown takes too long, force exit with process.exit(). Document the expected shutdown behavior.
    costmediumin proddelayed failureusers seeservice unavailablevisibilityvisible
    Sources[1]
  • Queue.obliterate · obliterate-active-jobs-error
    error
    Whenobliterate() called without opts.force=true while active jobs are processing
    ThrowsError('Cannot obliterate queue that has active jobs, use force option to force obliteration')
    Required handlingCaller MUST wrap obliterate() in try-catch. Drain the queue first (pause, wait for active jobs), or use obliterate({ force: true }) only when data loss is acceptable (test environments). Never call obliterate() in production request handlers without explicit environment guards.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[6]
  • Queue.obliterate · obliterate-irreversible
    error
    Whenobliterate() completes successfully in a production environment without environment guards
    ThrowsNo error — but all queue data is permanently deleted from Redis with no undo
    Required handlingCallers MUST add explicit environment guards: check NODE_ENV !== 'production' before calling obliterate(). Add admin confirmation for any production use. There is no undo — once obliterated, all job history is gone.
    costhighin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[7]
  • Queue.clean · clean-missing-grace-period
    error
    Whenclean() called with grace argument as undefined or null
    ThrowsError('You must define a grace period.')
    Required handlingAlways provide the grace period as first argument (milliseconds). Example: queue.clean(7 * 24 * 3600 * 1000, 'completed'). Wrap in try-catch in maintenance scripts and cron jobs.
    costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[6]
  • Queue.clean · clean-invalid-type
    error
    Whenclean() type argument is not one of: 'completed', 'wait', 'active', 'paused', 'delayed', 'failed'
    ThrowsError('Cannot clean unknown queue type <type>')
    Required handlingUse 'wait' (not 'waiting') for waiting jobs. Valid type strings are: 'completed', 'wait', 'active', 'paused', 'delayed', 'failed'. The monitoring API returns 'waiting' but clean() requires 'wait'. Wrap in try-catch to handle invalid type errors gracefully.
    costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[6]
  • Job.retry · retry-job-not-exist
    error
    WhenJob.retry() called on a job that no longer exists in Redis
    Required handlingCaller MUST wrap Job.retry() in try-catch. Verify job existence before retrying, or handle the not-exist error gracefully in admin retry flows.
    costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[1]
  • Job.retry · retry-job-not-failed
    error
    WhenJob.retry() called on a job that is not in the failed state
    Required handlingCaller MUST wrap Job.retry() in try-catch and handle the not-failed error. Check job.getState() before retrying, or catch and ignore this specific error in bulk retry flows.
    costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[1]
  • Queue.empty · empty-concurrent-add-race
    warning
    WhenQueue.empty() called while another process is concurrently adding jobs to the queue
    Required handlingCaller SHOULD pause the queue before emptying to prevent concurrent adds. Add: await queue.pause(); await queue.empty(); await queue.resume(). In single-process environments without concurrent writers, this is safe. In clustered environments, coordinate empty() with a distributed lock.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[6][8]
  • Queue.empty · empty-zombie-jobs-on-crash
    error
    WhenQueue.empty() called without wrapping in try-catch, and Redis connection drops mid-execution
    Required handlingCaller MUST wrap Queue.empty() in try-catch. On error, scan Redis for orphaned job keys and clean them up. Consider using Queue.obliterate({ force: true }) for a more complete cleanup in test environments, as obliterate uses Lua scripts that are more atomic than empty().
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[6]
  • Queue.removeRepeatable · remove-repeatable-silent-mismatch
    error
    WhenremoveRepeatable() called with RepeatOpts that do not exactly match the scheduled job's options
    Required handlingAfter calling removeRepeatable(), verify removal by calling queue.getRepeatableJobs() and checking that the job no longer appears. Store the exact RepeatOpts used at creation time (e.g. in a database) to ensure they can be reproduced exactly for removal.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[9][10]
  • Queue.removeRepeatableByKey · remove-repeatable-by-key-silent-noop
    warning
    WhenremoveRepeatableByKey() called with a key that no longer exists or was already removed
    Required handlingFor idempotent cleanup, the silent no-op behavior is acceptable by design. For critical schedule removal, verify with queue.getRepeatableJobs() after calling. Log the key being removed so that failed removals can be diagnosed via monitoring.
    costlowin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[9]
  • Job.progress · progress-job-key-missing
    error
    Whenjob.progress(value) called after the job has been deleted from Redis (e.g. removeOnComplete/removeOnFail removed it, or Redis eviction)
    Required handlingCallers SHOULD wrap job.progress() in try-catch within the processor function. Alternatively, avoid calling progress() after performing operations that might cause the job to be considered complete (e.g. don't call progress after the business operation that marks success — the job may have been cleaned up).
    costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[11]
  • Job.progress · progress-lock-lost
    error
    Whenjob.progress(value) called after the job's lock has expired or been taken by another worker
    Required handlingCaller SHOULD check for this error in progress() and immediately abort the processor function to prevent further duplicate work: try { await job.progress(50); } catch (err) { if (err.message.includes('Missing lock')) return; throw err; } Increase lockDuration and lockRenewTime to prevent premature lock expiry.
    costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[11]
  • Job.update · update-job-not-active
    error
    Whenjob.update(data) called on a job that is no longer in the active state
    Required handlingCaller MUST wrap job.update() in try-catch. Store critical intermediate state in external storage (database) rather than relying solely on job.update() — Redis TTL and job removal policies can destroy this data unexpectedly.
    costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[12][11]
  • Job.remove · remove-active-job-error
    error
    Whenjob.remove() called on a job that is currently being processed (active/locked)
    ThrowsError('Could not remove job <id>')
    Required handlingCaller MUST wrap job.remove() in try-catch. Check job state before removal: const state = await job.getState(); if (state !== 'active') { await job.remove(); } For active jobs, consider using queue.pause() to stop new jobs, then wait for the active job to finish before removing it.
    costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[12]
  • Job.promote · promote-not-delayed-error
    error
    Whenjob.promote() called on a job that is not in the delayed state
    ThrowsError('Job <id> is not in a delayed state')
    Required handlingCaller MUST wrap job.promote() in try-catch. Check job.getState() immediately before calling promote() and handle the 'not-delayed' error gracefully: const state = await job.getState(); if (state === 'delayed') { await job.promote(); }
    costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[12]
  • Job.log · log-job-key-missing
    warning
    Whenjob.log(row) called after the job has been deleted from Redis
    Required handlingCaller SHOULD await job.log() and wrap in try-catch, or attach .catch() to the returned promise. Pattern: await job.log('Step complete').catch(err => logger.warn('Log failed:', err.message)); This prevents unhandled rejections without blocking the processor.
    costlowin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[11]
  • Job.finished · finished-job-failed
    error
    WhenAwaiting Job.finished() when the job transitions to failed state
    Required handlingCaller MUST wrap await job.finished() in try-catch. On rejection, inspect the error message (which equals job.failedReason) and handle accordingly.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • Job.finished · finished-queue-closing
    error
    WhenQueue.close() is called while job.finished() promise is pending
    Required handlingCaller MUST wrap await job.finished() in try-catch. During shutdown, handle the queue-closing rejection gracefully rather than crashing. Consider a timeout pattern: Promise.race([job.finished(), shutdownTimeout]).
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[1]
  • Queue.whenCurrentJobsFinished · when-current-jobs-not-awaited-before-close
    error
    WhenQueue.close() called without first awaiting Queue.whenCurrentJobsFinished()
    Required handlingCaller MUST await queue.whenCurrentJobsFinished() before calling queue.close() in shutdown handlers: process.on('SIGTERM', async () => { await queue.whenCurrentJobsFinished(); await queue.close(); process.exit(0); });
    costhighin prodsilent failureusers seelost datavisibilitysilent
    Sources[6]
  • Queue.whenCurrentJobsFinished · when-current-jobs-no-sigterm-handler
    warning
    WhenwhenCurrentJobsFinished() not called in SIGTERM/SIGINT handler
    Required handlingRegister shutdown handlers that call whenCurrentJobsFinished() before exit: process.once('SIGTERM', async () => { await queue.whenCurrentJobsFinished(); await queue.close(); process.exit(0); });
    costmediumin prodsilent failureusers seelost datavisibilitysilent
    Sources[6]
  • Queue.removeJobs · remove-jobs-no-try-catch
    error
    Whenawait queue.removeJobs(pattern) called without try-catch
    Throwsioredis Error (Redis connection loss or queue.isReady() rejection)
    Required handlingCaller MUST wrap await queue.removeJobs(pattern) in try-catch. On Redis connection error, log and retry after reconnection. Zero matches is not an error.
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[12]
  • Queue.removeJobs · remove-jobs-pattern-blocks-redis
    warning
    WhenQueue.removeJobs(pattern) called with broad pattern on large Redis keyspace
    Required handlingAvoid removeJobs() with broad patterns in production during peak load. Prefer Queue.clean() with a specific status and grace period for bulk cleanup. If removeJobs() is required, use narrow patterns or schedule during off-peak hours.
    costmediumin proddegraded serviceusers seedegraded performancevisibilityvisible
    Sources[12]
  • Job.releaseLock · release-lock-not-owner
    error
    Whenjob.releaseLock() called when current worker does not own the job lock
    ThrowsError('Could not release lock for job <jobId>')
    Required handlingCaller MUST wrap await job.releaseLock() in try-catch. Only call releaseLock() from within the job's own processor function context or after acquiring the lock via job.takeLock(). Do not call releaseLock() from outside the processing context.
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[12]
  • Job.releaseLock · release-lock-no-try-catch
    error
    Whenawait job.releaseLock() called without try-catch
    Throwsioredis Error (Redis connection loss during Lua script execution)
    Required handlingCaller MUST wrap await job.releaseLock() in try-catch. Log and handle appropriately. On Redis connection error, do not retry — the lock expires via TTL automatically.
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[12]
  • Job.moveToFailed · move-to-failed-no-try-catch
    error
    Whenawait job.moveToFailed(errorInfo) called without try-catch
    ThrowsError (Redis Lua script error codes: -1 missing key, -3 wrong state, -6 lock mismatch)
    Required handlingCaller MUST wrap await job.moveToFailed({ message: err.message }) in try-catch. On -1 (missing key): job was already cleaned up — log and skip. On -3 (wrong state): job completed via another path — log and skip. On -6 (lock mismatch): another worker took ownership — do not retry.
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[12][11]
  • Job.moveToFailed · move-to-failed-retry-not-exhausted
    warning
    Whenjob.moveToFailed() called when job still has remaining retry attempts
    Required handlingIf the intent is to force the job to the failed state immediately (bypassing retries), call job.discard() first, then job.moveToFailed(). Without discard(), the job will retry per its configured attempts and backoff settings.
    costlowin prodsilent failureusers seelost datavisibilitysilent
    Sources[12]
  • Job.extendLock · extend-lock-zero-return-unchecked
    error
    Whenawait job.extendLock(duration) is called without checking the return value, and the function returns 0 (lock not extended because lock expired or was taken by another worker).
    ThrowsDoes not throw — returns 0 silently when lock extension fails
    Required handlingCaller MUST check the return value of extendLock(): const extended = await job.extendLock(30000); if (\!extended) { // Another worker took the job — stop processing immediately throw new Error('Lock lost: job taken by another worker'); } Without this check, duplicate processing causes duplicate emails, duplicate payments, or data corruption in distributed queue deployments.
    costhighin prodsilent failureusers seelost datavisibilitysilent
    Sources[13][6]
  • Job.extendLock · extend-lock-no-try-catch
    warning
    Whenawait job.extendLock(duration) called without try-catch and Redis connection is unavailable or the Redis command fails.
    ThrowsError (ioredis connection error or NOSCRIPT error if Lua script not loaded)
    Required handlingWrap extendLock() calls in try-catch. On Redis error, abort the current job processing and let Bull's stall detection handle requeuing: try { const extended = await job.extendLock(this.settings.lockDuration); if (\!extended) throw new Error('Lock lost'); } catch (err) { logger.error('Lock renewal failed, aborting job', err); return; }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[6]
  • Queue.getMetrics · get-metrics-metrics-not-enabled
    warning
    Whenqueue.getMetrics() called but metrics were not enabled in QueueOptions (no `metrics` key in queue constructor options).
    ThrowsDoes not throw — returns zeroed data structure silently
    Required handlingEnsure metrics are enabled in the queue constructor: const queue = new Queue('jobs', { metrics: { maxDataPoints: 1440 } }); Document to callers that zero results may mean metrics are disabled, not that no jobs have run.
    costlowin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[14][15]
  • Queue.getMetrics · get-metrics-no-try-catch
    warning
    Whenawait queue.getMetrics('completed') called without try-catch and Redis pipeline exec returns an error in the first or second result.
    ThrowsError (Redis pipeline error from multi.exec)
    Required handlingWrap getMetrics() in try-catch in dashboard/monitoring code: try { const metrics = await queue.getMetrics('completed'); return metrics.data; } catch (err) { logger.error('Failed to fetch queue metrics:', err); return []; }
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[14]
  • Job.moveToCompleted · move-to-completed-return-value-not-serializable
    error
    Whenjob.moveToCompleted(returnValue) called with a non-JSON-serializable returnValue
    ThrowsTypeError: Converting circular structure to JSON / TypeError: Do not know how to serialize a BigInt
    Required handlingCaller MUST ensure the returnValue is JSON-serializable before calling moveToCompleted(). Use try-catch and validate the return value: // WRONG — throws if result contains circular references or BigInt await job.moveToCompleted(processResult); // CORRECT — validate before completing try { JSON.stringify(processResult); // test serialization } catch (err) { // Return a safe summary instead of the full object await job.moveToCompleted({ status: 'completed', error: 'result-not-serializable' }); return; } await job.moveToCompleted(processResult); Common causes: circular references in database ORM objects (Prisma result objects may have circular prototype chains), BigInt fields from PostgreSQL, or custom class instances with non-serializable properties.
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[12]
  • Job.moveToCompleted · move-to-completed-no-try-catch
    error
    Whenawait job.moveToCompleted() called without try-catch, and the job is no longer in the active state, the lock has expired, or the lock is owned by another worker.
    ThrowsError('Missing key for job <id> finished') — job deleted from Redis (removeOnComplete, eviction); Error('Missing lock for job <id> finished') — lock expired before completion; Error('Job <id> is not in the active state. finished') — job completed/failed via another path; Error('Lock mismatch for job <id>...') — another worker took the lock.
    Required handlingCaller MUST wrap await job.moveToCompleted() in try-catch. Handle each error type: - Missing key: log and skip (job was cleaned up by another mechanism) - Missing lock / Lock mismatch: another worker owns the job — abort immediately to prevent duplicate completion; do not re-attempt moveToCompleted. - Wrong state: job already completed or failed — check getState() before retrying. try { await job.moveToCompleted(returnValue); } catch (err) { if (err.message.includes('Missing lock') || err.message.includes('Lock mismatch')) { logger.warn('Lock lost before completion, aborting', { jobId: job.id }); return; // Stop processing — another worker will handle this job } if (err.message.includes('Missing key')) { logger.warn('Job cleaned up before completion', { jobId: job.id }); return; // Job was removed (removeOnComplete with short TTL) } throw err; // Unexpected error — re-throw }
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[12][11]
  • Job.takeLock · take-lock-false-return-unchecked
    error
    Whenawait job.takeLock() is called without checking the return value, and returns false (lock held by another worker or lock already acquired by this worker).
    ThrowsDoes not throw — returns false silently when lock cannot be acquired
    Required handlingCaller MUST check the return value. false means the lock could not be acquired: // WRONG — proceeds even if lock was not acquired await job.takeLock(); await doExclusiveWork(); // NOT safe — another worker may also be doing this! // CORRECT — check return value before proceeding const lock = await job.takeLock(); if (!lock) { logger.warn('Could not acquire lock for job, another worker owns it', { jobId: job.id }); return; // Abort — do not proceed with exclusive work } await doExclusiveWork(); await job.releaseLock(); // Always release in finally block Failing to check the return value leads to duplicate processing when multiple workers attempt to take the same lock — the same critical race condition as extendLock returning 0 without being checked.
    costhighin prodsilent failureusers seelost datavisibilitysilent
    Sources[12]
  • Job.takeLock · take-lock-no-try-catch
    warning
    Whenawait job.takeLock() called without try-catch and Redis connection is unavailable
    ThrowsError (ioredis connection error or NOSCRIPT error if Lua script not cached)
    Required handlingWrap await job.takeLock() in try-catch. On Redis error, do not proceed with exclusive work — treat it as a lock acquisition failure and abort: let lock; try { lock = await job.takeLock(); } catch (err) { logger.error('Redis error acquiring job lock', { jobId: job.id, err }); return; // Cannot determine lock state — abort } if (!lock) { return; } // Lock held by another worker
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[12]
  • Queue.pause · pause-no-try-catch
    error
    Whenawait queue.pause() called without try-catch and Redis connection is unavailable
    ThrowsError (ioredis connection error from isReady() or scripts.pause() Lua execution)
    Required handlingWrap await queue.pause() in try-catch in shutdown handlers and maintenance scripts. A Redis error during global pause means the pause state was NOT persisted — other workers will continue processing jobs. Retry or alert on failure: try { await queue.pause(); logger.info('Queue paused globally'); } catch (err) { logger.error('Failed to pause queue — workers may still be processing', err); // Alert ops team — jobs are still processing despite shutdown attempt }
    costmediumin prodimmediate exceptionusers seedegraded performancevisibilityvisible
    Sources[6]
  • Queue.pause · pause-local-hangs-on-stuck-job
    warning
    Whenqueue.pause(true) called with isLocal=true (without doNotWaitActive=true), and there is a job currently in the active state that never completes (stuck/hung processor).
    ThrowsDoes not throw — hangs indefinitely awaiting whenCurrentJobsFinished()
    Required handlingWhen using local pause in shutdown handlers, always set a timeout and use doNotWaitActive=true for immediate termination, or race with a timeout: // RISKY — hangs forever if a job is stuck await queue.pause(true); // SAFE — use doNotWaitActive for immediate local pause await queue.pause(true, true); // Returns immediately without waiting // SAFE — with timeout for graceful-but-bounded shutdown await Promise.race([ queue.pause(true), new Promise((_, reject) => setTimeout(() => reject(new Error('Pause timeout')), 30000)) ]).catch(() => queue.pause(true, true)); // Force pause if timeout
    costmediumin proddelayed failureusers seeservice unavailablevisibilityvisible
    Sources[6]

Sources

Every postcondition cites at least one of these. Numbered to match the footnotes above.

  1. [1]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull
  2. [2]blog.logrocket.com/asynchronous-task-processing-in-node-js-with-bullhttps://blog.logrocket.com/asynchronous-task-processing-in-node-js-with-bull/
  3. [3]docs.bullmq.io/guide/jobshttps://docs.bullmq.io/guide/jobs/stalled
  4. [4]github.com/luin/ioredishttps://github.com/luin/ioredis
  5. [5]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/issues/1731
  6. [6]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/queue.js
  7. [7]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/REFERENCE.md
  8. [8]raw.githubusercontent.com/OptimalBits/bullhttps://raw.githubusercontent.com/OptimalBits/bull/master/REFERENCE.md
  9. [9]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/repeatable.js
  10. [10]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/scripts/removeRepeatable-2.js
  11. [11]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/scripts.js
  12. [12]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/job.js
  13. [13]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/commands/extendLock-2.lua
  14. [14]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/lib/getters.js
  15. [15]github.com/OptimalBits/bullhttps://github.com/OptimalBits/bull/blob/master/README.md
Need a different package?
Request a profile