Skip to content
Get started

Why a cron job fails silently — and how to catch it

A cron job fails silently because cron does not alert you when a job fails or when it never runs at all — it just discards the output (or mails it to a local mailbox nobody reads). The two silent-failure classes are a job that runs and errors without anyone seeing the error, and a job that stops running entirely (a removed crontab line, a disabled schedule, a rebooted box). The reliable fix is a dead man's switch: the job reports success to an external monitor, and the monitor alerts you when that report doesn't arrive.

Why do cron jobs fail silently?

  • cron discards output by default: unless you redirect stdout/stderr to a file or a monitor, the error a failing job prints goes nowhere you'll see. cron may mail it to the local user's mailbox, which on most servers is never read.
  • cron doesn't alert on failure: a non-zero exit produces no notification. Nothing emails, pages, or flags you — the job just didn't work.
  • cron can't tell you about a run that never happened: if the crontab entry was removed, the schedule disabled, or the machine was down at the scheduled minute, there is no run and therefore no output at all — the most invisible failure of them all.
  • A script that ignores errors: without `set -e`, a script continues past a failed command and can exit 0, actively reporting success while the real work failed.

How do I catch a silent cron failure?

Use a dead man's switch. Instead of trying to catch every possible error inside the job, invert it: the job reports success to an external monitor, and the monitor alerts you when success doesn't arrive on schedule. That single pattern catches both silent classes at once — a job that errors withholds its success ping, and a job that never runs sends nothing at all.

the dead man's switch pattern
#!/usr/bin/env bash
set -euo pipefail                # fail loudly, don't mask errors

/usr/local/bin/nightly.sh        # the real work; aborts the script on any error

# Only reached on full success. If the job errors OR never runs, no ping arrives
# and the monitor alerts you that the run missed.
curl -fsS -m 10 --retry 3 "https://ping.cronshield.com/<your-check-id>"

How do I make failures visible too, not just misses?

Capture the output so a failure has evidence, and send a failure signal explicitly so you learn about an error the moment it happens rather than at the next expected window:

#!/usr/bin/env bash
set -uo pipefail
if /usr/local/bin/nightly.sh >> /var/log/nightly.log 2>&1; then
  curl -fsS -m 10 "https://ping.cronshield.com/<your-check-id>"          # success ping
else
  curl -fsS -m 10 "https://ping.cronshield.com/<your-check-id>?fail=1"   # failure ping — alert now, with the log
fi
On the free tier the miss/fail alert tells you the run didn't succeed. On paid tiers, CronShield ingests the captured log and puts the last log line plus a likely cause in the alert — turning a silent failure into a triaged one. PING_URL is a placeholder for the endpoint you get on a monitor.

Catch this failure automatically

The free tier gives you a heartbeat endpoint and an email alert when an expected ping doesn't arrive. Paid tiers add the log-aware diagnosis — the last log line and a likely cause in the alert. The heartbeat receiver ships in an upcoming release; see the plans to learn what each tier adds.

Frequently asked questions

Why didn't cron email me when my job failed?
cron only mails output to the job's local user mailbox, and only if a mail transfer agent is configured — on most modern servers it isn't, so the mail goes nowhere. Even when it works, that mailbox is rarely read. cron never sends an external notification on failure by itself.
How do I know if a cron job stopped running entirely?
You can't tell from cron — a run that never happens leaves no trace. An external heartbeat monitor is what surfaces it: when the expected success ping doesn't arrive in its window, the monitor alerts you, whether the job errored or the schedule silently stopped.