You added a heartbeat to your nightly job and the dashboard is reassuringly green. But green only means a ping arrived. Whether that ping represents actual success depends entirely on how your shell computes exit codes — and the defaults will quietly lie to you.
The trap: pinging regardless of exit code
The most common mistake is a wrapper that always pings after the job, no matter what happened:
# WRONG — pings even when the job failed run_job.sh curl -fsS https://notdown.notdown-app.workers.dev/ping/YOUR-KEY
Because the two commands are independent, curl runs whether run_job.sh succeeded, crashed, or was killed. The switch sees a healthy heartbeat forever while your job is broken. Tie the ping to success instead, or send the real exit code:
# right — success or failure is reported honestly run_job.sh; curl -fsS https://notdown.notdown-app.workers.dev/ping/YOUR-KEY/$?
$? is the exit status of the previous command. Zero means success; anything else is a failure the monitor should act on now.
set -e doesn't do what you think inside a script
Many scripts start with set -e ("exit on error") and assume that guarantees a nonzero exit on any failure. It doesn't, in a few important cases. A command whose failure is "used" by the shell — the condition of an if, part of a &&/|| list, or negated with ! — will not trigger set -e. And a function called in one of those contexts loses set -e for its whole body. So a script can sail past a real error and still exit 0.
The pipefail gotcha
This is the one that catches everyone. In a pipeline, the shell reports only the exit code of the last command by default:
# exits 0 even though the database dump failed — # gzip succeeded compressing the error output pg_dump mydb | gzip > backup.sql.gz echo $? # 0 (!!)
If pg_dump dies, gzip still happily compresses whatever it received and exits 0, so the pipeline exits 0, so your heartbeat reports a healthy backup that is actually empty. Fix it with pipefail, which makes a pipeline fail if any stage fails:
set -o pipefail pg_dump mydb | gzip > backup.sql.gz echo $? # nonzero if pg_dump failed
The robust preamble for any job you monitor is:
set -euo pipefail
-e exits on error, -u treats unset variables as errors (catches typos in paths), and -o pipefail propagates failures through pipes. It's three words that turn a lying heartbeat into an honest one.
Also verify the output, not just the exit code
Even a correct exit code can hide a "successful" run that produced nothing. A dump that writes zero rows, an export that yields a 0-byte file — both can exit 0. For jobs where "it ran" and "it worked" differ, assert on the result and fail explicitly:
set -euo pipefail pg_dump mydb | gzip > backup.sql.gz test "$(stat -c%s backup.sql.gz)" -gt 1000 # fail if suspiciously tiny curl -fsS https://notdown.notdown-app.workers.dev/ping/YOUR-KEY/$?
The rule
Silence should never read as success, and neither should exit 0 by default. Make your shell compute an honest exit code, send that code to your monitor, and let a real failure trip the alert immediately. A heartbeat is only as truthful as the $? behind it.
NotDown's ping URL accepts an explicit signal — /ping/KEY/$? for the real exit code, /ping/KEY/fail to trip immediately, /ping/KEY/start to mark that a job began. See the heartbeat docs →