Hung job detection: alert when a job starts but never finishes
A hung job isn't a failure — yet. The process is alive, so no error fires and no
alert sends. It's stuck on a database lock, a full-disk write that blocks forever,
or a stale NFS mount. Meanwhile, the next scheduled run can't start, and you won't
know until someone notices that the job that "always completes by 4am" is still
running at noon. The fix: send a /start ping
when the job begins and a success ping when it ends. If the success doesn't arrive
within the expected window, LastPing opens an incident while the job is still running.
Why hung jobs are the hardest failure to catch
Every monitoring tool that fires on errors is blind to a hung job. The job hasn't errored — it's just stuck.
No failure signal to detect
Exception trackers, log alert rules, and on-failure pings all need the job to exit or throw. A job stuck on a blocking call never exits, never throws, and never sends anything. The monitoring stack sees a healthy job.
The next run can't start
Most schedulers prevent a new instance from starting while the previous one is running. A job stuck for 10 hours blocks every subsequent run — compounding the problem until the hung job is found and killed.
The deadline passes unnoticed
If a job is expected to finish by 04:00 and it's still running at 08:00, whatever it was supposed to produce (a backup, a report, a data sync) is four hours late — and nobody knows unless they check manually.
The start/success ping pattern
Two pings per job: one at the start, one at the end. The gap between them is your overrun detection window.
Create a monitor with an appropriate grace window
In the console, create a heartbeat monitor with your job's schedule. Set the grace window to the maximum expected runtime, not just the expected completion delay. If your backup normally takes 5 minutes but you want to alert if it exceeds 30, set grace to 30 minutes. Copy the ping URL.
Wrap your command with a start/exit-code reporter
The /start ping arms overrun detection. The exit-code ping maps
0 to success and anything else to failure — one pattern covers both
failure detection and hung detection.
Combine with a timeout for resource safety
Overrun detection tells you the job is stuck. A timeout wrapper
kills it to prevent resource exhaustion. Together they cover both alerting and
automatic recovery:
Python / Node.js: inline start + success pings
For in-process jobs, send the pings from code:
What the start/success pattern catches
Three signals, three failure modes covered — all with a single monitor.
- Job never started — no
/startping in the expected window → incident opens. Crontab lost, machine down, scheduler misconfigured — all caught. - Job started but is hung —
/startarrived; no success within grace → incident opens while the job is still running. Overrun detection fires. - Job failed —
/failor a non-zero exit code ping closes the run with failure → incident opens immediately. - Recovery — next successful run closes the incident automatically. No manual reset.
- Flap protection — a transient failure that recovers within the damping window doesn't page you repeatedly.
Running GitHub Actions? The workflow_run
webhook delivers started and completed events — no YAML changes needed. See
GitHub Actions monitoring.
For the conceptual overview of why absence is a better signal than failure, see
dead-man's switch explained and
silent failure in scheduled jobs.
For AI agents that need to report their own heartbeat, the
LastPing MCP server exposes all three signals as MCP tools.
Questions people ask
Front-loaded answers — the most important fact first.
-
How do I detect a hung job?
Send a
/startping at the beginning of the job and a success ping at the end. Set the grace window to your maximum expected runtime. If the success ping doesn't arrive within the grace window, LastPing opens an incident even though the job is technically still running. This is overrun detection. -
What is the difference between a hung job and a failed job?
A failed job exits with a non-zero code and produces a signal. A hung job is still running — its process is alive — so it produces no failure signal. Error monitoring can catch failures; only overrun detection can catch hung jobs, because it uses time (the gap between start and expected completion) as the trigger.
-
What causes jobs to hang?
Common causes: (1) database lock wait — waiting for a lock held by a long-running transaction; (2) full-disk write — writes block indefinitely on some filesystems; (3) stale NFS mount — reads and writes block forever; (4) network timeout too long — waiting for a response that will never come with a default socket timeout of minutes or hours; (5) reading from stdin when not attached to a terminal.
-
Why not just set a timeout on the job?
A timeout is a good safety net — use
timeout 3600 /usr/local/bin/backup.shto kill a stuck job automatically. But a timeout alone doesn't alert you; it just kills the job and exits with code 124. Combine both: timeout for automatic cleanup, start/success pings for alerting. -
Is this free?
Yes. LastPing is free and fully hosted with no monitor cap and no paid tier. See how it compares to Healthchecks.io and Cronitor.
Know the moment a job gets stuck — not when someone notices it's late.
One wrapper script, one heartbeat monitor. Set up overrun detection in minutes — and LastPing monitors itself the same way.