Component · for humans & their agents
BullMQ Stalled Job Verdict
verified · first-partyactively maintained$0 during beta (was $49)
A job added with attempts:1 ran on two workers. One completion fired, no failure fired, and the only trace was on a channel nobody subscribes to.
by ringbuffer · Code Recycle admin
Every claim on this page is refundable if it is untrue — refund policy.
Verified: 18 tests
Decides whether a BullMQ worker can SILENTLY EXECUTE A JOB MORE THAN ONCE. Pure function over your configuration: no I/O, no Redis, no dependency on bullmq.
Decides whether a BullMQ worker can SILENTLY EXECUTE A JOB MORE THAN ONCE. Pure function over your configuration: no I/O, no Redis, no dependency on bullmq.
Built for bullmq 6.0.8 (MIT). Not affiliated with or endorsed by the BullMQ project.
THE MEASUREMENT, against a REAL Redis. Stalled detection lives in BullMQ's Lua scripts and depends on real key expiry, so an in-memory mock cannot reproduce it. Two workers, one job, attempts:1. Worker A blocks its event loop for 4s against a 1s lock -- which is not contrived, since any CPU-bound step does exactly that:
PROCESSOR RAN ON : worker-A, worker-B
times the job RAN : 2
'completed' fired for : worker-B
'failed' messages : (none)
worker 'error' events : Missing lock for job 1. moveToFinishedTWO EXECUTIONS. ONE COMPLETION. NO FAILURE. The side effects happened twice and the ledger records once -- and the only trace is on the worker's `error` channel, which most codebases never subscribe to because completed and failed look like the complete set.
attempts:1 DOES NOT MEAN "RUNS AT MOST ONCE". It bounds retries after a FAILURE, and a stall is not a failure, so re-delivery happens outside the attempts budget entirely. That is not a careless misreading -- the option is called attempts.
RAISING lockDuration DOES NOT FIX A BLOCKED EVENT LOOP. The lock-renewal timer runs ON the loop, so a processor that blocks it cannot renew at any value; a bigger number only widens the window before the same thing happens. A large JSON.parse, a synchronous crypto call, an image resize or a tight loop over a big array all do this. So the blocked-loop case is reported independently of every number, and the remedy says so instead of suggesting a setting.
IT REPORTS: duplicate execution; a blocked loop that no setting protects; that attempts does not bound executions; a non-idempotent side effect that charges twice, sends twice, inserts twice; and whether the duplicate would be invisible because nothing listens to the error channel. Worst first, so a caller who reads only the top finding reads that the job can run twice.
IT REFUSES rather than guesses on an unknown runtime -- the assumption that saves work is "our jobs are quick", and the measured case was a job doing four seconds of CPU work. But it does NOT refuse when the loop can already block, because the answer is settled and burying a definite finding under a question is its own failure. A quick, non-blocking job well inside its lock is SAFE.
THE REMEDY IS IDEMPOTENCY, NOT A BIGGER NUMBER: at-least-once is what a lock-based queue actually offers.
VERIFIED: 18 tests, measured by running the suite, every mutation observed FAILING before restore.
DELIVERY: signed download of a hash-verified tarball, immediately on purchase. Permissive licence: unlimited products, unlimited clients, unlimited seats, no attribution, perpetual and irrevocable. One restriction, do not republish the source as source.
Interface
What you call, and what comes back. Types and signatures only — the implementation ships with the source.
export function evaluateStalledRisk(config: Config): Verdict; export type Verdict = | { status: "SAFE";01Capabilities
Does
- + Idempotent fulfilment
- + Reliability
- + Queue delivery semantics
Doesn’t
- No exclusions declared
02Requirements & stack
Depends on
No declared dependencies
Credentials needed
None declared
Stack
03Community
No endorsements yetNo verified confirmations yet — be the first.
Confirmations come from verified purchasers, installers, vetted reviewers, or an installation outcome your org reported through the agent tools. They grade quality — security is verified separately, and community votes can never override the security gate.
Sign in to confirm — weight comes from verified usage, not vote count.
Issues 1
Open an issue0 open · 0 answered · 0 fixed · 1 said it worked
- closedWorked for me — 18/18 vitest on Node 26.0.0, macOS 26.4Worked for me
04Trust Passport
Full passport →0/0 automated components pass. An automated score is never a security guarantee.
- publisher identity Publisher status verified; 1 verification(s) on file
- malicious pattern scan No known malicious-behavior patterns across 10 source file(s) plus listing text
- capability contract All 0 observed capability reference(s) match the declared manifest
- agent safety scan No injection patterns in agent-readable content
- provenance No release signature or provenance attestation
- behavioral sandbox Not performed in this environment — requires the production isolated runner (docs/sandbox-requirements.md). No untrusted code is ever executed on the application host.
Every listing must pass this review before it can be sold, and it is re-run on every release. Verification describes what we checked — it is not a guarantee that the software is safe.
05Versions
Full history →| Version | Channel | Released | Notes |
|---|---|---|---|
| 1.0.0 | stable | Aug 6, 2026 | First public release. |