Introduction

GitHub Actions scheduled workflows are easy to misdiagnose because a failed cron trigger often leaves no workflow run at all. The workflow file can be syntactically valid and still never execute if it lives off the default branch, the repository went inactive, or the schedule is concentrated on congested top-of-hour times where delayed runs are more common.

Symptoms

  • A workflow with on.schedule never appears in the Actions run history
  • Manual workflow_dispatch works, but the cron schedule does not
  • The schedule started failing after a default branch rename or a long quiet period
  • Teams expected a run at an exact minute, but GitHub never created one

Common Causes

  • The cron expression uses the wrong field count or the wrong UTC assumption
  • The workflow file is not on the default branch
  • The workflow is disabled or the repository has hit scheduled-workflow inactivity behavior
  • The schedule is technically valid but clustered on a busy minute where runs are delayed or skipped

Step-by-Step Fix

  1. 1.Validate the cron expression and remember GitHub uses UTC
  2. 2.GitHub Actions schedules use five cron fields, not system-cron six-field syntax. Keep the first version simple so you can prove the schedule works before optimizing it.
yaml
on:
  schedule:
    - cron: "17 * * * *"
  workflow_dispatch:
  1. 1.Confirm the workflow file is enabled and lives on the default branch
  2. 2.Scheduled workflows only run from the default branch revision, so a valid cron in a feature branch will never fire on its own.
bash
gh workflow view nightly.yml
gh workflow enable nightly.yml
  1. 1.Check repository inactivity and recent workflow state changes
  2. 2.If the repository was quiet for a long period, scheduled workflows may stop until someone re-enables them or new activity resumes.
bash
gh run list --workflow nightly.yml --limit 20
  1. 1.Move away from top-of-hour schedules and add a manual fallback
  2. 2.A schedule like 0 * * * * competes with many other repositories. Picking an offset minute reduces queue pressure and makes missed triggers easier to notice.
yaml
on:
  schedule:
    - cron: "17 */6 * * *"
  workflow_dispatch:

Prevention

  • Keep every scheduled workflow paired with workflow_dispatch for manual testing
  • Store scheduled workflow files on the default branch only
  • Prefer offset minutes over 0 * * * * when exact top-of-hour execution is not required
  • Alert on missing expected runs instead of assuming GitHub will always execute every cron minute exactly