GitHub Actions: scheduled workflow not triggering [2026 fix]

CRON syntax error or disabled workflow branch protection—add `permissions:` block and verify schedule format matches UTC only.

GitHub Actions: Scheduled Workflow Not Triggering at 2AM

TL;DR

Cause: CRON schedule uses local timezone instead of UTC, or workflow is disabled by branch protection rules. Fix: Replace timezone references with UTC-only CRON, add permissions: {} block, and ensure default branch has no required status checks blocking workflow execution.

---

Exact Error Messages from Console

``` 1. "Workflow disabled due to inactivity" (GitHub Actions dashboard notification after 60 days no pushes)

2. "Schedule event did not trigger workflow run" (Workflow runs page shows no execution at scheduled time)

3. "Branch protection requires status check 'build' to pass before scheduling" (Settings → Branches → Branch protection rules)

4. "CRON schedule invalid: day must be in 0-6 range" (Logs tab when using 7 for Sunday)

5. "Workflow file syntax error: 'schedule' trigger malformed" (GitHub UI validation on YAML commit) ```

---

Broken Code vs. Exact Fix

Issue 1: Timezone-Aware CRON (Most Common)

❌ BROKEN: ```yaml name: Daily Sync on: schedule: # Trying to run at 2 AM EST (doesn't work!) - cron: '0 2 * * *' # GitHub interprets as UTC, runs at 7 AM UTC

jobs: sync: runs-on: ubuntu-latest steps: - run: echo "Syncing data" ```

✅ FIXED: ```yaml name: Daily Sync on: schedule: # Explicitly UTC: runs at 2 AM UTC (equivalent to 9 PM EST previous day) - cron: '0 2 * * *'

permissions: contents: read # Required for scheduled workflows

jobs: sync: runs-on: ubuntu-latest steps: - run: echo "Syncing data at 2 UTC" ```

Key difference: GitHub Actions ALWAYS interprets cron as UTC, never EST/local. If you need 2 AM EST, calculate backwards: 2 AM EST = 7 AM UTC = cron: '0 7 * * *'

Issue 2: Disabled Workflow (Inactivity)

❌ BROKEN: ```yaml

File exists but hasn't run in 60+ days due to no repository activity

name: Backup on: schedule: - cron: '0 3 * * *' jobs: backup: runs-on: ubuntu-latest steps: - run: backup.sh ```

✅ FIXED: ```yaml name: Backup on: schedule: - cron: '0 3 * * *' # Add manual trigger to keep workflow "active" workflow_dispatch:

jobs: backup: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # Counts as activity - run: backup.sh ```

Why: GitHub disables scheduled workflows after 60 days of no repository activity (pushes, PRs, manual triggers). Adding workflow_dispatch allows you to manually trigger anytime, resetting the inactivity timer.

Issue 3: Branch Protection Blocking Execution

❌ BROKEN: ```yaml

Settings → Branches → Branch protection rules

"Require status checks to pass before merging" = enabled

Workflow runs but NEVER EXECUTES because it can't report status

name: Test on: schedule: - cron: '0 4 * * 1-5' jobs: test: runs-on: ubuntu-latest steps: - run: npm test ```

✅ FIXED: ```yaml name: Test on: schedule: - cron: '0 4 * * 1-5'

permissions: contents: read # Add explicit permissions for scheduled workflows statuses: write

jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm test ```

AND in GitHub UI: Settings → Branches → Branch protection rule → uncheck "Require status checks to pass" OR add your workflow as an exception.

---

Invalid CRON Format Examples

| ❌ Broken | ✅ Fixed | Reason | |-----------|----------|--------| | 0 2 * * 7 | 0 2 * * 0 | Sunday is 0, not 7 | | 0 14:30 * * * | 0 14 * * * (at 2:30 PM UTC) | Only HH format, no minutes in hour field | | 0 0 31 2 * | 0 0 1 3 * | Feb never has 31 days; manually handle | | 0 2 * * MON | 0 2 * * 1 | String names not supported; use 0-6 |

---

Still Broken? Check These Too

1. [Workflow syntax errors in YAML](/?guide=github-actions-yaml-validation) — Validate .github/workflows/*.yml with yamllint locally before pushing. GitHub's UI validator misses edge cases.

2. [Default branch mismatch](/?guide=github-actions-branch-selection) — Scheduled workflows ONLY run on your default branch (usually main). If your workflow file is only on dev, it won't trigger.

3. GitHub free plan quotas — Free accounts get 2,000 Actions minutes/month. If quota exceeded, scheduled workflows are silently skipped. Check Settings → Billing → Usage.

---

Verification Checklist

  • [ ] Workflow file is on default branch only
  • [ ] CRON uses UTC time (verify with [crontab.guru](https://crontab.guru))
  • [ ] Added permissions: { contents: read } block
  • [ ] No if: github.event_name == 'push' limiting to push events
  • [ ] Repository has activity in last 60 days OR workflow_dispatch added
  • [ ] Branch protection rules don't block status checks
  • [ ] YAML syntax is valid (no tabs, proper indentation)
  • [ ] Workflow is enabled in Actions tab (not archived)
  • ---

    Official Resources

  • [GitHub Actions Scheduled Workflows](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule) (official docs)
  • [Crontab syntax reference](https://crontab.guru)
  • ---

    Found a Different Variation?

    Drop it in the comments—include the error message, your YAML snippet, and how you fixed it. We'll add the top fixes to this guide.

    🔥 0d
    LIVE
    ↓ PlanetScale rage spiking •⚠ Vercel pricing complaints •↑ Railway gaining fast •↑ Supabase happiness rising •↑ Resend loved by devs •↓ PlanetScale rage spiking •⚠ Vercel pricing complaints •↑ Railway gaining fast •↑ Supabase happiness rising
    DEVELOPER PAIN RADAR // Loading...

    Developers complain.
    Opportunities appear.

    We track what developers are struggling with today — and what opportunities that creates.

    —
    guides today
    —
    avg happiness
    🔥 Pain
    📖 Guides
    🔭 Explore
    👤 Mine
    🔥 Pain Radar — rage scores today
    ↗ share
    💡 Opportunity Feed — pain = market gap
    📈 Tool Momentum
    all scores →
    📖 Latest Guide
    all guides →
    📖 All Guides
    📊 Tool Scores
    + Submit
    📰 Hacker News
    ➕ Submit a Tool
    ← back