GitHub Actions workflow not triggering: three different states
A GitHub Actions workflow not triggering is three different situations wearing one description, and the first thing to establish is whether a run object exists, because only one of them creates one. A run that exists and concluded immediately, a run that exists and is waiting, and no run at all need three different investigations.

What this error means
You pushed, or opened a pull request, or dispatched by hand, and the thing you expected to see is not there. Sometimes the Actions tab is simply empty for that commit. Sometimes a run card is present with a red marker and nothing to expand, because the run has no jobs. Sometimes the run is there and queued, and stays queued. The three look similar in a glance at a list and are completely different underneath, which is why the advice you find for one of them does nothing for the other two.
gh api repos/OWNER/REPO/actions/runs --jq '.workflow_runs[0] | {name, status, conclusion}'
{"name":"ci","status":"completed","conclusion":"startup_failure"}Ask the API before you read the page
The Actions tab is a poor instrument here, because it renders all three states as an absence of the thing you wanted. The runs endpoint is decisive: if the list holds nothing for your commit, no run was created, and every explanation involving the contents of your workflow file is off the table except the ones about whether the file was eligible to run at all.
If a run is there with a conclusion of startup_failure, the run was created and immediately concluded before any job existed. That state has its own page in this hub, which covers what fails at that moment and which ref the file is read from, so it is not repeated here.
If a run is there with a status of queued and no conclusion, the run exists and the jobs are waiting for a machine. Nothing is wrong with the file. That is a runner availability, concurrency or environment approval question, and the annotations are not on the file but on the job.
| What you observe | Run object exists | Where the answer is |
|---|---|---|
| Nothing listed for the commit | no | the trigger, the ref and whether the workflow is enabled |
| A run card with no jobs under it | yes, concluded | the run's own annotation, see the startup_failure page |
| A run that stays queued | yes, still running | runner availability, concurrency or a waiting environment |
| A run on the wrong commit | yes, elsewhere | which ref raised the event |
Common causes
The workflow file is not on the ref that raised the event
A push to a branch reads the workflow files on that branch. A file that exists only on main does not run for a push elsewhere, and a file deleted on a branch stops running there while still running on main. Nothing is reported, because there was nothing to report against.
The event is one that only counts on the default branch
Schedules and manual dispatches are taken from the default branch, so a new schedule or workflow_dispatch block has no effect until it merges. The workflow looks correct on the branch you are testing and does nothing, which reads exactly like a broken trigger.
The workflow or the repository has been switched off
A disabled workflow, a scheduled workflow disabled after a long quiet period in a public repository, or a policy that restricts which workflows may run, all produce silence rather than failure. Each is a setting rather than a file, which is why reading the file repeatedly gets you nowhere.
A run exists and you are looking at the wrong surface
In our experience a good share of missing runs are runs that did happen, on a different commit or under a different workflow name, or runs whose absence from a pull request is a property of the checks surface rather than of the run. Query the runs endpoint before concluding anything.
How to fix it
Query the runs endpoint for the exact commit
Ask the API rather than the UI, filtering by the head SHA you pushed. An empty list and a list with one concluded run are different problems, and this is the cheapest way to tell which you have.
gh api "repos/OWNER/REPO/actions/runs?head_sha=$(git rev-parse HEAD)" \
--jq '.workflow_runs[] | {name, event, status, conclusion}'Confirm the file exists on the ref that raised the event
- Identify the ref the event came from, which for a pull request is the head branch and for a push is the pushed branch.
- List the workflow directory on that ref rather than in your working copy.
- Check that the
onblock on that ref subscribes to the event you triggered. - If the trigger is a schedule or a manual dispatch, check the default branch instead, because those are read from there.
git ls-tree --name-only origin/my-branch .github/workflows/Check the switches before you edit the file again
Look at the workflow's own page for a disabled state, and at the repository and organization Actions settings for a policy that restricts what may run. A workflow in a public repository that runs only on a schedule can also be disabled automatically after a long period without repository activity, which is a separate page in this hub.
When a run exists and concluded at startup, read that page instead
A conclusion of startup_failure means the run was created and could not be prepared, which is a different investigation with its own annotation and its own set of causes. Follow the link in the related pages below rather than continuing here, because nothing about triggers or refs applies once a run object exists.
The reasons no run object is created
A workflow only runs from a file that exists on the ref the event came from. A branch that does not carry the file yet produces no run for a push to that branch, however correct the file is on main, and this is the single most common version of the empty Actions tab during a first setup.
The event has to be one the file subscribes to, and several events only count on the default branch. A schedule and a manual dispatch are read from the default branch, so a workflow that adds either one on a feature branch offers no button and fires no timer until it merges.
Then there are the switches. A workflow can be disabled by hand and stays listed but inert. A scheduled workflow in a public repository is disabled automatically after a long enough period without repository activity. And a repository or organization can restrict which actions and workflows may run at all. None of these produce a failure, because nothing was attempted.
Why nothing appears on the pull request either
The status checks reference lists startup_failure among the statuses a check can hold and says in the same row that it does not apply to check runs. It is a property of the suite. That has a practical consequence people rarely connect to the symptom: when a run concludes at startup, the pull request gains no check run to click, because check runs are created per job and no job was created.
So a required status check waits forever with nothing behind it, and the contributor sees a merge button they cannot press and no red mark anywhere to explain it. The hub page on required checks covers that side of the problem, including which entries block a merge and which do not.
The practical instruction is to stop looking at the pull request and look at the run list for the head commit. The pull request can only show you check runs, and in two of the three states there are none.
Why there is no recorded run on this page
This page is about the absence of runs, and an absence cannot be recorded. Two of the three states produce no runner log by definition: one creates no run object at all, and one creates a run with no jobs. The third produces a run that is still waiting, so its log does not exist yet either. There is no moment at which a Latchkey runner could have been holding a camera.
What can be shown instead is the instrument, which is the API call above. It is the same call in all three states and its reply is what separates them, so reproducing it costs a reader nothing and proves more than a screenshot of an empty tab.
How to prevent it
- Add a new trigger on the default branch first, then use it from branches.
- Keep one query, against the runs endpoint by head SHA, as the first step of every investigation.
- Do not rely on a pull request's checks box to tell you whether a run happened.
- Review the disabled state of scheduled workflows in quiet public repositories.
Frequently asked questions
How do I tell whether a workflow run was created at all?
Why does my new workflow_dispatch button not appear?
workflow_dispatch block added on a feature branch offers no button until the change merges. The same applies to schedule, which is why a new cron on a branch never fires.Why is there no red mark on the pull request when a run failed at startup?
startup_failure is documented as a check suite status and explicitly not applicable to check runs. Check runs are created per job, no job was created, so there is nothing for the pull request to display. Look at the run list for the head commit instead.