The Sweeper Pattern: How I Reconcile Crashed n8n Jobs Without Guessing
Description and use
The Sweeper pattern automatically reconciles crashed n8n jobs by scanning expired leases and verifying outcomes with external providers, avoiding duplicate actions.
Problem
Crashed n8n jobs leave rows in an ambiguous "claimed" state, making it unclear whether the external action (email, charge, webhook) succeeded.
Outcome
Implemented a sweeper that reliably reconciles ambiguous job states, reducing duplicate external actions and providing a clear "review" state for human inspection.
Workflow
- Create jobs table with lease_seconds default 600
- Worker atomically claims a queued job by updating state to "claimed" and setting claimed_at
- Sweeper runs on schedule, selecting jobs where state is "claimed" and claimed_at is older than lease_seconds
- For each expired job, determine provider class and available handle
- If forced-unique provider with idempotency key or provider reference, re-drive or query provider to confirm success and set state to "done"
- If suggestion-key provider with deterministic handle, look up provider; if found set to "done", if not found after settle window set to "review"
- If suggestion-key provider with no handle, set to "review"
- Ensure single-flight by using a sweep_locks table or atomic claim on sweeper rows
- Handle kill scenarios by asserting provider-side counts and avoiding duplicate effects
Evidence
A sweeper is a scheduled job that scans for work left in an ambiguous state and decides its fate.
Workers claim atomically — only the worker that gets the row proceeds:
And the sweeper touches only expired leases:
The lease is the line between "the worker is slow" and "the worker is dead." Ten minutes is my default.
- Record date: 2026-10-11T19:10:52.359Z