GitHub Actions scheduled workflow not running? Why, and how to get alerted

You added on: schedule, the workflow ran fine for a while, and then it didn't. Or it never ran at all, or it ran an hour late. GitHub won't email you about a run that never started. Here's what usually causes it, how to check in two minutes, and how to add a heartbeat so you're told the first time it happens.

Updated November 4, 2026 · GitHub Actions on github.com · 7-minute read

Short answer: check that the workflow is enabled and on the default branch, and that your cron time is in UTC. Public repositories have scheduled workflows switched off after 60 days without activity, and any schedule can start late under load. To catch it next time, end the job with a step that pings a heartbeat monitor on success and a /fail URL on failure. If the ping doesn't arrive on time, you get an alert.

Why scheduled workflows don't run

SymptomLikely causeFix
Never ran at allThe workflow file (or the schedule trigger) is only on a feature branch. Scheduled workflows run on the latest commit of the default branch.Merge it to the default branch.
Ran at the "wrong" timeSchedules are evaluated in UTC by default. 0 9 * * * is 9:00 UTC, not 9:00 where you are.Convert to UTC and recheck when daylight saving changes, or set a time zone on the schedule if your GitHub plan supports the timezone key (check the current docs).
Stopped after weeks of workingIn public repositories, GitHub disables scheduled workflows after 60 days with no repository activity. The Actions tab shows a banner; nothing is emailed by default.Re-enable in the Actions tab or with gh workflow enable.
Stopped, but still shows as enabledGitHub's troubleshooting docs note that a scheduled workflow can stop when its associated actor account (usually whoever last changed the cron) is suspended, deleted or deprovisioned, even though manual runs still work.Have someone with write access commit a change to the cron line.
Starts 10 to 60+ minutes late, or skips a runGitHub documents that scheduled events can be delayed during high load, most often at the top of the hour, and that some queued runs may be dropped.Avoid minute 0, and set a realistic grace period.
Disabled or silently ignoredSomeone disabled the workflow, the YAML or cron expression is invalid, Actions is disabled for the repository, or it's a fork where scheduled workflows don't run until Actions is enabled there.Check gh workflow list --all and the Actions tab for errors.
Ran, "succeeded", produced nothingA script swallowed an error, an API returned zero rows, a step was skipped by an if:.Report a count and alert on zero (see below).

The shortest schedule Actions allows is once every 5 minutes. If you need tighter timing than "roughly when I asked", a scheduled workflow is the wrong tool; trigger it from outside with workflow_dispatch or repository_dispatch instead.

Check it right now

With the GitHub CLI signed in to the repository:

# Is the workflow enabled? Disabled ones show "disabled_inactivity" or "disabled_manually"
gh workflow list --all

# Did any scheduled runs happen recently?
gh run list --workflow nightly.yml --event schedule --limit 10

# Re-enable a workflow GitHub switched off
gh workflow enable nightly.yml

# Is the file (with the schedule) on the default branch?
gh repo view --json defaultBranchRef --jq .defaultBranchRef.name
git log origin/main -1 -- .github/workflows/nightly.yml
  1. Enabled? If gh workflow list --all shows the workflow as disabled, that's your answer.
  2. Any scheduled runs at all? No rows from gh run list --event schedule means the trigger never fired: check the branch and the YAML.
  3. Late or missing ones? Compare the run start times (shown in UTC) with your cron in UTC.
  4. Ran but wrong? Open the latest run and check that the steps that do the work actually ran and produced output.

Get alerted when a scheduled run is missed

GitHub can notify you when a run fails, but a disabled workflow, a dropped run or a schedule on the wrong branch produces no run, so there's nothing to fail. A heartbeat monitor flips it around: the workflow checks in after each good run, and silence raises the alert. The examples use a Pulsecheck ping URL like https://pulsecheck.run/p/YOUR-JOB-ID, but any heartbeat service works the same way.

1. Work out the expected schedule in UTC

17 19 * * * is once a day at 19:17 UTC. Pick a grace period that covers GitHub's delays plus your run time: 30 to 60 minutes is sensible for most scheduled workflows.

2. Create a monitor and store its URL as a secret

In Pulsecheck, add a job with that period and grace and copy its ping URL. Save it as a repository secret (Settings › Secrets and variables › Actions) called PULSECHECK_URL, so it isn't sitting in your public YAML.

3. Add a success heartbeat step

Make it the last step, with if: success(). POSTing a small JSON body like {"count": 42} lets an output rule such as count at least 1 flag a run that succeeded with nothing in it as Ran but output looks wrong. (Output checks are on Pulsecheck's Pro and Agency plans; missed-run and /fail alerts work on every plan, including the free one.)

4. Add a failure heartbeat step

A step with if: failure() that POSTs a link to the run to /fail means you hear about a broken run immediately instead of waiting for the grace period to run out, and the alert takes you straight to the logs.

5. Bound the job with timeout-minutes

The default job timeout is 360 minutes. Set timeout-minutes to a little over your longest normal run, so a hung step fails, triggers the failure step, and frees the runner.

6. Test with workflow_dispatch

Add workflow_dispatch so you can run it by hand from the Actions tab or with gh workflow run nightly.yml. Confirm the monitor turns green, then make a step fail on purpose and confirm the /fail alert arrives.

Copy-paste workflow

Replace build_report.py with your own work, and add PULSECHECK_URL as a secret first.

name: nightly-report
on:
  schedule:
    - cron: "17 19 * * *"     # 19:17 UTC = 06:17 in Melbourne during daylight saving
  workflow_dispatch: {}       # lets you run it by hand to test

jobs:
  report:
    runs-on: ubuntu-latest
    timeout-minutes: 30       # a stuck run fails instead of running for hours
    steps:
      - uses: actions/checkout@v4

      - name: Build report
        id: build
        run: |
          python build_report.py > out.txt
          echo "rows=$(wc -l < out.txt)" >> "$GITHUB_OUTPUT"

      - name: Heartbeat (success)
        if: success()
        run: |
          curl -fsS -m 10 --retry 3 -X POST "${{ secrets.PULSECHECK_URL }}" \
            -H "Content-Type: application/json" \
            -d '{"count": ${{ steps.build.outputs.rows }}, "run": "${{ github.run_id }}"}'

      - name: Heartbeat (failure)
        if: failure()
        run: |
          curl -fsS -m 10 --retry 3 -X POST "${{ secrets.PULSECHECK_URL }}/fail" \
            -H "Content-Type: text/plain" \
            --data-binary "Run failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"

If the job is cancelled (for example by a newer run in the same concurrency group), neither heartbeat step runs, and the missed-ping alert covers it. Running the same job somewhere else? See the guides for cron jobs, scheduled AI agents and n8n workflows.

Common mistakes

FAQ

Why is my GitHub Actions cron schedule not running?

The most common reasons are: the workflow file isn't on the default branch, the cron time is in UTC rather than your local time, GitHub disabled the workflow after 60 days without repository activity (public repositories), the workflow was disabled by hand, or the YAML is invalid. Check gh workflow list --all and gh run list --event schedule first.

What time zone does a GitHub Actions schedule use?

Schedules are evaluated in UTC by default. A cron of 0 9 * * * runs at 9:00 UTC, which is evening in Australia and early morning in the US. Convert your local time to UTC and remember that daylight saving changes the offset, or use the timezone key on the schedule where GitHub supports it.

Why did my scheduled workflow run late?

GitHub documents that scheduled events can be delayed during periods of high load, especially at the start of every hour, and that some queued runs may be dropped. Pick an odd minute such as 17 instead of 0, and give your monitor a grace period of 30 to 60 minutes.

Why did GitHub disable my scheduled workflow?

In public repositories, scheduled workflows are automatically disabled when there has been no repository activity for 60 days. GitHub shows a banner in the Actions tab; re-enable it there or with gh workflow enable. A heartbeat monitor catches it the first day it stops.

Does a scheduled workflow run on branches other than main?

No. Scheduled workflows run on the latest commit on the default branch. A schedule that only exists in a feature branch, or in a renamed workflow file that hasn't been merged, never fires.

How do I get alerted when a scheduled GitHub Action doesn't run?

GitHub's own notifications only cover runs that started and failed. To catch a run that never started, add a final step that pings a heartbeat monitor on success and a /fail URL on failure. If the success ping doesn't arrive on schedule plus a grace period, the monitor alerts you.

Know the moment a scheduled workflow goes quiet.

Pulsecheck gives every scheduled workflow its own ping URL and alerts you by email (or Slack on Pro and Agency) when a run is missed, fails, or comes back empty. Free for 3 jobs.

Start free

Related: Cron job monitoring · How to know if your AI agent stopped running · How to monitor an n8n workflow that fails silently · all Pulsecheck guides. Schedule behavior described here follows GitHub's documentation for the schedule event at docs.github.com as of November 4, 2026.