Scheduled workflow disabled after 60 days of inactivity
A scheduled workflow disabled after 60 days leaves no red run behind, because the cron simply stopped being scheduled. GitHub applies this to public repositories that have had no activity, and the workflow stays off until somebody turns it back on.

What this error means
Whatever the cron produced stops updating and nothing in the Actions tab is red. The run history ends on an ordinary green run and then goes quiet, which is why this is usually noticed downstream: a nightly report that is weeks stale, a mirror that has stopped syncing, a dependency scan whose last result predates a release. In the Actions tab the workflow itself carries a banner saying it is disabled. The REST API is more precise and worth preferring, because it distinguishes this from the three other ways a workflow can be off, and it does so in a single field.
This scheduled workflow is disabled because there hasn't been activity in this repository for at least 60 days. Enable this workflow to resume scheduled runs.One field tells you which kind of off it is
GitHub publishes the workflow schema in its REST API description, and the state property carries an enum of exactly five values. Three of them mean the workflow will not run, and they have different causes and different fixes, so this is the first thing to read rather than the banner.
state | What put it there | How it clears |
|---|---|---|
active | Normal | Nothing to do; if the cron is still not firing, the cause is elsewhere |
disabled_inactivity | A public repository with no activity for 60 days | Enable the workflow |
disabled_fork | The repository is a fork of a public repository | Enable it, if you actually want the fork running it |
disabled_manually | Somebody turned it off | Enable it, and find out why it was turned off |
deleted | The workflow file is gone | Restore the file |
Common causes
A public repository went 60 days without activity
The documented policy, and the one this page is named for. It is most likely on a repository whose only job is the cron itself, since a repository that exists to publish a nightly artifact may receive no commits for months at a time. The state reads disabled_inactivity.
The repository is a fork, so the schedule never started
Different state, same silence. Scheduled workflows are disabled by default when a public repository is forked, so a fork has never run the upstream's cron rather than having stopped. The state reads disabled_fork, and enabling it is a decision about whether you want the fork doing that work.
Somebody disabled it
The state reads disabled_manually, and it can be weeks old with nobody remembering. In our experience this happens during an incident, when a noisy workflow is switched off to stop it making things worse, and then nobody switches it back on.
It is active, and the schedule was never going to run
Worth ruling out before you go looking for a policy. A scheduled workflow only runs on the default branch, so a cron added on a feature branch and never merged has no effect. A workflow whose file is on the default branch but whose state is active and whose history is empty is this case, not the policy.
How to fix it
Read the state before you touch the file
- List the repository workflows through the API and read the
statefield for the one that stopped. - Match it against the table above; the three disabled values have three different stories.
- If it reads
active, stop looking at the policy and check that the file is on the default branch.
Enable it, then run it once by hand
Re-enabling resumes the schedule from the next tick, which can be a day away and leaves the gap unfilled. A dispatch run immediately afterwards backfills whatever the cron would have produced, so the output is fresh before anyone downstream notices.
Give the workflow a dispatch trigger alongside the schedule
This costs one line and is what makes the previous fix possible. It also gives you a way to test a cron change without waiting for it to fire.
on:
schedule:
- cron: '15 4 * * *'
workflow_dispatch:Alert on the output, not on the run
The failure mode here is the absence of runs, which run-based alerting cannot see. Check the age of whatever the cron produces, from something that is not the cron, and the next sixty-day disable becomes an alert rather than an archaeology exercise.
The rule, as GitHub states it
The policy is three sentences in GitHub's documentation, and each sentence is doing work: "To prevent unnecessary workflow runs, scheduled workflows may be disabled automatically. When a public repository is forked, scheduled workflows are disabled by default. In a public repository, scheduled workflows are automatically disabled when no repository activity has occurred in 60 days."
The qualifier in the last sentence is the one people miss. It says "In a public repository", so a private repository is not subject to this at all, and a workflow that stopped firing there is disabled_manually, on a non-default branch, or not the problem you think it is. The middle sentence is the separate disabled_fork case, which is why a freshly forked repository never runs the upstream's nightly jobs.
Confirm it before you change anything
One call settles it, and it is worth running rather than working from the banner, because the state field names which of the three it is in a single value. List the workflows with their states, then act on the one that is not active.
# which workflows are off, and why
gh api repos/OWNER/REPO/actions/workflows \
--jq '.workflows[] | select(.state != "active") | "\(.state) \(.path)"'
# turn it back on
gh workflow enable .github/workflows/nightly.yml
# and run it once now rather than waiting for the next tick
gh workflow run .github/workflows/nightly.ymlKeeping it awake
Re-enabling fixes today and not the next sixty days. The mechanisms people reach for are all variations on giving the repository activity, and they trade differently: a periodic commit is reliable and noisy, a bot that re-enables the workflow on a schedule is quiet but needs its own credentials and its own schedule, and moving the job off the public repository sidesteps the policy entirely because it does not apply to private ones.
Whichever you pick, add a check that the thing the cron produces is fresh, because that is the signal that actually failed here. A workflow that is disabled produces no run, so any alert built on a failed run will never fire. An alert on the age of the output would have caught this on day sixty-one.
Why there is no recorded run on this page
Reproducing this means owning a public repository, leaving it untouched for sixty days and photographing the result, which is not a run and not something a runner does. There is also nothing for a runner to repair: the workflow was never dispatched, so no job existed to retry. What is checkable instead is the state value, which is why this page quotes the enum from GitHub's own API description and the banner text from a public repository that hit it.
How to prevent it
- Watch the freshness of the artifact the schedule produces, since a disabled workflow emits no failed run to alert on.
- Keep
workflow_dispatchon every scheduled workflow so a gap can be backfilled without editing the cron. - Check the
statefield of critical workflows periodically, rather than trusting the Actions tab banner. - Move a cron that matters onto a repository that gets commits, or accept that it needs re-enabling.
Frequently asked questions
Does the 60 day disable apply to private repositories?
state field will say which.How do I tell an auto-disabled workflow from one someone turned off?
state field on the workflow in the REST API. GitHub documents five values, and disabled_inactivity is the 60 day policy, disabled_manually is a person, and disabled_fork is the default for a fork of a public repository. One field separates all three, which is why it is worth a call rather than a look at the Actions tab.Why did my forked repository never run the scheduled workflow?
disabled_fork. Enabling it is a deliberate choice, and worth thinking about, since the upstream's nightly job may not be something your fork should be doing.Will a scheduled run count as activity and keep the workflow enabled?
Related guides
References
- GitHub Actions: events that trigger workflows, the schedule event and its notes
- GitHub Actions: disabling and enabling a workflow, in the UI, the API and the CLI
- GitHub REST API description: the workflow schema and its five state values
- camptocamp/docker-postgres#28: the banner text from a repository disabled for four months
- GitHub Actions documentation