GitHub Actions on section missing from a workflow file
A GitHub Actions on section missing from a workflow file ends the run before it starts, and the layer that refuses it is not the one most people assume. The published schema marks only the jobs key as required at the workflow root, so the refusal comes from GitHub rather than from anything in the parser you can read.

What this error means
Every push to every branch produces a failed check against a workflow you did not think was running. That is the shape worth recognizing, because a workflow with no usable trigger does not quietly sit there: it is evaluated, found unusable and reported, on every push, forever. The file is often one somebody meant to disable. Commenting the whole thing out, emptying it while leaving it in place, or deleting the contents and keeping the name all produce the same result, which surprises people who expected an empty file to be ignored.
Error
No event triggers defined in `on`What the published schema actually requires
Follow the workflow root definition in the schema that ships in the runner and the list of properties is short: the trigger key, a name, a description, a run name, defaults, env, permissions, concurrency and jobs. Exactly one of them carries the required flag, and it is jobs. The trigger key is listed as an ordinary optional property.
The editor copy of the schema in the language services repository has the same root definition with the same single required property, which is why an editor extension does not warn you about a file with no trigger. Both published parsers will read such a file without objecting.
The refusal therefore happens somewhere else. GitHub evaluates the file when it decides which workflows a push affects, and a file that offers no usable event is reported at that point. That component is not published, so the honest statement is that the rule is GitHub behavior rather than parser behavior.
The message it produces is the one quoted above, and what it leaves out is the diagnosis. There is no file path, no line and no column, which is unusual for this validator: the key is not there to point at. A recorded experiment that walked a workflow file up from zero bytes got those two lines from the empty file, and something else from the next rung, where the key exists with nothing under it. That rung gets the ordinary shape, a position and an unexpected empty value, because an empty value is a value. So the distinction that matters is whether the key survived, and parentheses and numbers in your annotation mean it did.
The rest of this page is the other half: why the file looks fine to every tool you have locally, and which edits produce it.
| Layer | Requires a trigger | What it does with a file that has none |
|---|---|---|
| YAML parsers generally | no | parses it, no complaint |
| The published workflow schema | no, only jobs is required | reads it, no complaint |
| The editor schema copy | no, the same root definition | no warning in the editor |
| GitHub, when deciding what a push runs | yes | reports it against every push |
Common causes
The trigger block was commented out to disable the workflow
The commonest origin by a distance, and the one place the wording of the edit decides which message you get. Comment out the key along with its events and no key is left, which is this message. Comment out only the events, as below, and the key survives with nothing under it, which the ladder answers differently. Either way the file fails against every push rather than doing nothing, which is the opposite of the intent.
# the trigger key survived, every event under it did not
on:
# push:
# branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo helloThe file was emptied but not deleted
A commit that strips the contents and leaves the path in place. The diff reads as a deletion in review, and the zero byte file is still evaluated on every push.
The trigger key drifted out of the root mapping
One extra level of indentation puts it inside another key. The YAML is valid, the block looks right in isolation, and the root genuinely has no trigger.
The file is not a workflow at all
A template, a fragment or an editor configuration file stored in the workflows directory. Everything in that directory is treated as a workflow, so a file that was never meant to be one fails as one.
How to fix it
Decide whether the workflow should exist
- If it should run, restore a trigger block at the root of the file.
- If it should not run for now, disable the workflow rather than editing it.
- If it should never run again, delete the file.
- If it is not a workflow, move it out of the workflows directory.
Restore a trigger at the root, not inside another key
Check the indentation of the trigger key against the jobs key. Both belong at column one, and a trigger that is indented further is inside something else regardless of how correct the block under it looks.
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latestDisable rather than comment out
Disabling leaves the file intact and stops it being considered, which is what commenting out the events was trying to achieve. It is reversible from the same command.
gh workflow disable ci.yml
gh workflow enable ci.ymlKeep non workflow files out of the workflows directory
Templates and fragments belong beside it, not in it. A directory whose every file is a workflow is one fewer thing to explain to the next person who adds a file there.
The quoting advice, and why it does not apply here
There is widespread advice to quote the trigger key because YAML will otherwise read it as a boolean. That advice has a real origin and it does not apply to this parser, which is worth stating plainly because acting on it wastes time you could spend on the actual cause.
The runner YAML reader resolves plain scalars using the YAML 1.2 core schema, and the source says so in a comment with a link to the specification. Its boolean matcher accepts six spellings and no others: true, True, TRUE, false, False and FALSE. The words on, off, yes and no are not among them, so the trigger key is read as the ordinary string it looks like.
The advice comes from YAML 1.1, where those four words are booleans, and from the many tools that still implement it. A script that loads your workflow with a 1.1 parser really does see a key that is not the trigger key, and linters warn about it for that reason. That is a fact about your tooling, not about GitHub, and quoting the key is harmless but it will not fix a missing trigger.
# the runner reads these six spellings as booleans, and no others
true True TRUE false False FALSE
# so this key is a string to the workflow parser
on:
push:
branches: [main]The edits that produce it
Commenting out the trigger block is the first. Somebody wants to stop a workflow temporarily, comments out the events and leaves the key, and now the file has a trigger key with nothing under it. Reports of this describe a failure on every push to every branch, which is exactly what it should do, and it is startling because the intent was to make the workflow stop.
Emptying the file is the second. A commit that removes the contents of a workflow and leaves a zero byte file behind produces the same thing, and the diff looks like a deletion, so reviewers read it as one.
Indentation is the third and the least obvious. A trigger key that has drifted one level in, so that it sits under something else rather than at the root, is a perfectly valid mapping in the wrong place. The root then has no trigger key, and the misplaced one is a stray key inside whatever now contains it.
The remedy for all three is the same and it is not to edit the trigger block. If you want a workflow to stop running, delete the file or disable the workflow, which are both operations that leave nothing behind to be evaluated on every push.
# stop a workflow without leaving a file that fails on every push
gh workflow disable ci.yml
# or remove it entirely
git rm .github/workflows/ci.ymlWhy there is no recorded run on this page
A workflow with no usable trigger does not produce a run with jobs in it, so there is no log to capture. The observable artifact is a failed check attached to a commit, and a capture of ours would show our repository name and our commit, which carries no information about yours.
The parts of this page that can be established from published sources are established from them: the single required property at the workflow root is read out of both copies of the schema, and the six boolean spellings are read out of the reader that implements the YAML 1.2 core schema. The part that cannot be, which is what GitHub does with the file, is labeled as GitHub behavior rather than dressed up as parser behavior.
Nothing here is repairable by a runner. No runner is ever handed this workflow, because the decision is made before any job is created.
How to prevent it
- Never disable a workflow by commenting out its trigger block.
- Treat an emptied workflow file in a diff as a file that still runs.
- Keep only real workflows in the workflows directory.
- Check that the trigger key and the jobs key sit at the same indentation.
Frequently asked questions
Is the on key required in a GitHub Actions workflow?
Do I need to quote the on key so YAML does not read it as true?
Why does an empty workflow file fail on every push?
Is this the same as writing the trigger key with nothing under it?
How do I stop a workflow without breaking every build?
Related guides
References
- actions/runner: the published workflow schema, workflow-root and its one required property
- actions/runner: YamlObjectReader.cs, the YAML 1.2 core schema boolean matcher
- GitHub Actions: workflow syntax, on
- GitHub Actions: disable and enable a workflow
- susam.net, Minimal GitHub Workflow: the recorded ladder from a zero-byte file upward
- GitHub Actions documentation