Skip to content
Code Recycle

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

Get it free — beta

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. moveToFinished

TWO 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

typescript

03Community

No endorsements yet

No 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.

Open an issue

Sign in to confirm — weight comes from verified usage, not vote count.

0 open · 0 answered · 0 fixed · 1 said it worked

04Trust Passport

Full passport →
–/100

0/0 automated components pass. An automated score is never a security guarantee.

✓ Verified · first-partyreviewed Sep 20, 2026 · re-verification due Dec 19, 2026
  • 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.

VersionChannelReleasedNotes
1.0.0stableAug 6, 2026First public release.