Your GitHub Actions cron didn't run. Get told next time.
The schedule trigger is best-effort and silent. A three-line heartbeat makes it accountable.
Why scheduled workflows don't run
- Delays and drops at busy times. GitHub documents that scheduled events can be delayed during periods of high load, and some queued runs may be dropped. Top-of-the-hour schedules are the most affected.
- Auto-disabled after 60 days. In public repositories, scheduled workflows are disabled automatically when there's been no repository activity for 60 days.
- Default branch only. Schedules run from the default branch; a workflow edited on another branch never fires.
- The job runs but produces nothing — an empty export, a skipped step, a
continue-on-errorthat hid a failure.
Add a heartbeat step
Add curl steps that ping /start first, the ping URL on success() and /fail on failure(). Create a Did It Run? watch with the same cron expression in UTC and a grace window of 30–60 minutes to absorb GitHub's normal delays.
Check the output, not just the exit code
Send your job's output file as the ping body (--data-binary @output.txt) and set a minimum size or required keyword. A green run with an empty report raises an empty alert.
Tip: avoid the top of the hour
Schedule at an odd minute (e.g. 17 6 * * *) — it's less congested — and mirror it on the watch.
FAQ
Should I keep the ping URL secret?
Yes. Store it as a repository or organization secret and reference it as ${{ secrets.HEARTBEAT_URL }}.
What grace period should I use?
30 minutes is a sensible start for GitHub schedules; increase it if you see occasional late runs.
Can one watch cover a matrix of jobs?
Use one watch per job you care about, or ping once from a final job that depends on all the others.
Know the next time it doesn't run
Know when your AI agents and automations didn't run. Free for 3 watches.
Start free