Retry limit set with dead letter escalation
Why it matters
An unbounded retry limit — or no limit at all — allows a job to cycle indefinitely, consuming worker capacity and queue resources without ever resolving. CWE-770 covers allocation of resources without limits; an infinite retry loop is a resource exhaustion vector against the queue infrastructure itself. CWE-391 covers insufficient logging of errors — a job that fails silently after an unbounded retry chain leaves no trace for operators. ISO-25010:2011 reliability.recoverability requires that failed states are detectable and recoverable; an undocumented failure is neither.
Severity rationale
High because an unbounded retry limit can hold worker concurrency slots indefinitely and provides no path for operators to inspect or replay permanently failed sends.
Remediation
Set an explicit attempts limit between 3 and 10 on the BullMQ queue, keep failed jobs, and fire an alert when the limit is reached:
export const emailQueue = new Queue('email', {
connection,
defaultJobOptions: {
attempts: 5,
backoff: { type: 'exponential', delay: 2000 },
removeOnFail: false
}
})
worker.on('failed', async (job, err) => {
if (job && job.attemptsMade >= (job.opts.attempts ?? 5)) {
logger.error({ jobId: job.id, data: job.data }, 'Job exhausted retries')
await alertOps(`Email job ${job.id} permanently failed: ${err.message}`)
}
})
Never set attempts: Infinity or omit the attempts field — both produce equivalent unbounded behavior.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Sending Pipeline & Infrastructure Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe · CWE-770 — Allocation of Resources Without Limits or Throttling
- cwe · CWE-391 — Unchecked Error Condition
- iso-25010:2011 · reliability.recoverability — Recoverability
Taxons
History
- 2026-04-18·v1.0.0·Initial import from sending-pipeline-infrastructure·automated