Skip to content
Latchkey LogoLatchkey home

GitHub Actions invalid workflow file on this ref

GitHub Actions invalid workflow file on this ref is the case where the same file passes on one branch and is rejected on another. A workflow is validated against the ref it runs on, and everything it pulls in resolves at that ref too, so a file you have not touched can become invalid.

Why one branch validates and another does not, and where the required check stops
The file is not validated once. Each ref is validated on its own, against whatever its references resolve to at that moment, and only the failing ref loses its check.

What this error means

A required check never reports. The pull request sits with a check that is expected and never arrives, the merge button stays disabled, and nothing is red, because a run that dies during startup has no failing job to be red. Worse, the same workflow is demonstrably fine: it runs on the default branch and it ran on this branch last week. The only visible trace is a run in the Actions tab with a conclusion that is neither success nor failure, no jobs at all, and a single annotation naming your workflow file and a line number. That annotation is the entire diagnosis, and the pull request page does not show it.

Invalid workflow file annotation, quoted from mosip/esignet#2551
Invalid workflow file: .github/workflows/push-trigger.yml#L88
The workflow is not valid. .github/workflows/push-trigger.yml (Line: 88, Col: 21):
Invalid input, NODE_VERSION is not defined in the referenced workflow.
.github/workflows/push-trigger.yml (Line: 89, Col: 16):
Invalid input, ZIP_DIR is not defined in the referenced workflow.

A minimal workflow that produces it

This file is written for this page and has never been run. Read on its own it is unremarkable: it calls a shared workflow at a moving ref and passes two inputs. It is valid the day it is written and stays valid until somebody in the other repository removes one of those inputs, at which point every ref still carrying this version becomes invalid and the updated refs do not.

.github/workflows/ci.yml (illustrative)
name: ci
on:
  push:
  pull_request:

jobs:
  build:
    uses: octo-org/shared/.github/workflows/npm-build.yml@develop
    with:
      service: web
      node_version: "22"
      zip_dir: dist
    secrets: inherit

Common causes

A referenced workflow or action changed underneath a moving ref

The commonest version, and the one that makes the failure feel like it came from nowhere. The caller pins to a branch or a floating tag, the upstream file changes its inputs, and every ref carrying the old caller becomes invalid. Nothing in the failing repository was edited.

The branch carries an older version of the workflow

A long-lived release branch or a stale pull request branch keeps the file as it was, including references since corrected on the default branch. The fix landed only where it was applied, so the default branch is healthy and every other ref is not.

The workflow was edited on one ref and not merged

A change made on a branch, or a pull request that fixes the workflow and is itself blocked by it, leaves the two refs disagreeing. It feels circular: the check that would let you merge the fix is the one the fix repairs.

The check comes from an event that does not count

A run triggered by an event outside the eligible list reports checks that never satisfy a required status check, however green they are. Different cause, same symptom: a merge box waiting forever.

How to fix it

Find the run that has no jobs

  1. Open the Actions tab and filter by the branch, not by the pull request.
  2. Look for a run with no jobs listed and a conclusion that is not success or failure.
  3. Open it and read the annotation: it names the file, the line and the column on the ref that failed.
Terminal (illustrative)
$ gh run list --branch release-2.0.x --limit 10
$ gh run view <run-id>

Compare the file across the two refs

When one branch validates and another does not, the difference is in the file or in what the file resolves to. A diff answers the first in one command; if the files are identical, a reference is floating.

Terminal (illustrative)
$ git diff origin/main..origin/release-2.0.x -- .github/workflows/

Pin what you call, then update it deliberately

A floating reference makes every ref in the repository depend on the current state of another one. Pinning converts that into a change you approve, and an updater turns it into a pull request. The alternative is a branch that breaks on somebody else's schedule.

Fix every ref that carries the broken caller

Landing the fix on the default branch hides the symptom rather than ending it, because each branch validates its own copy. List the branches that still carry the old reference and cherry-pick the correction onto each, or accept that they cannot build.

Terminal (illustrative)
$ git branch -r --list 'origin/release-*'
$ git grep -l 'npm-build.yml@develop' $(git branch -r --list 'origin/release-*') -- .github/workflows

Validation happens per ref, not per repository

A workflow is evaluated as it exists on the ref that triggered it, together with everything it references resolved at that moment. That is usually invisible, because most repositories keep one version of a workflow. It becomes visible when two refs carry different versions of the same file, or the same version of a file whose dependencies have moved underneath it.

mosip/esignet#2551 is that story with the receipts. A caller still passed two inputs to a shared workflow at a moving ref; the shared workflow had dropped both weeks earlier. One release branch had already pushed and had run nothing since, and the issue marks two more branches broken on their next push, with only the branch that took the fix healthy. Nothing had changed in the repository that broke.

The documentation explains why the failure repeats rather than happening once: "Ensure that you only commit valid workflow files to your repository. If .github/workflows contains an invalid workflow file, GitHub Actions generates a failed workflow run for every new commit."

The corrected file, pinned rather than floating

The correction is not in the inputs, it is in the reference. Pinning the call to a tag or a commit means the shared workflow it resolves to is the one this branch was tested against, so a change upstream arrives as a pull request rather than as a branch that stops validating. Read the two samples as a pair: the first is valid until somebody else edits a file in another repository, the second until you decide otherwise.

Where the inputs really have moved, the second half of the fix is to update the caller on every ref that still uses the old ones. Fixing the default branch makes the symptom disappear from view while every long-lived release branch stays broken.

.github/workflows/ci.yml, corrected (illustrative)
jobs:
  build:
    uses: octo-org/shared/.github/workflows/npm-build.yml@v4.2.0
    with:
      service: web
    secrets: inherit

Why the pull request shows nothing

A run that fails during startup never creates a check run for any job, so branch protection has nothing to see. A check run that is expected is "waiting for a status to be reported", and the check suite carries startup_failure: "The check suite failed during startup. This status is not applicable to check runs."

The troubleshooting guide describes the same dead end from the merge box: when a required job never produces a check, "the pull request is blocked with \"Waiting for status to be reported.\"" It also lists the events whose checks are eligible at all, the other reason a check you can see in the Actions tab does not count: push, pull_request, pull_request_review, pull_request_target, deployment and deployment_status.

From the command line the state is easier to see. The GitHub CLI prints a run with no jobs and a failing conclusion as "This run likely failed because of a workflow file issue.", the nudge toward the annotation that the pull request page never gives you.

What you seeWhat it meansWhere the detail is
a run with no jobsstartup, not a job, failedthe run annotation
conclusion startup_failurethe check suite never startedthe run annotation
a required check expectedno check run was ever createdthe Actions tab, not the pull request
valid on one branch onlya reference resolved differently therethe caller on that ref
failing on every new committhe file is invalid, not the committhe workflow file on that ref

Why there is no recorded run on this page

There is a run and it has nothing in it. Validation failed before any job was created, so there is no log to capture and no runner was ever involved. The annotation at the top is quoted from mosip/esignet#2551, which names the branch, the two lines and the run it came from.

How to prevent it

  • Pin called workflows and third-party actions, so no ref of yours depends on the current state of someone else's.
  • Validate workflow files in a pre-commit hook with a linter that parses the expression grammar, not just the YAML.
  • Treat a required check as a contract and avoid requiring one that a workflow can fail to produce.
  • Sweep long-lived release branches when a workflow fix lands, because each one validates its own copy.

Frequently asked questions

Why is my GitHub Actions workflow invalid on one branch only?
Because each ref is validated on its own: the file on that branch, and everything it references resolved at that ref, has to be valid together. A caller pinned to a moving upstream ref can become invalid without anybody editing it, which is what mosip/esignet#2551 reported.
What does startup_failure mean in GitHub Actions?
That the check suite failed before any job was created. The status reference defines it as "The check suite failed during startup. This status is not applicable to check runs." There are no jobs and no logs, only a run-level annotation naming the file and the line.
Why is a required check stuck waiting for a status to be reported?
Because no check run was ever created for it. A check run in the expected state is "waiting for a status to be reported", and a workflow that dies at startup produces none, so branch protection waits forever.
How do I see the error when a run has no jobs?
Open the run itself rather than the pull request and read the annotation, or use the CLI, which prints "This run likely failed because of a workflow file issue." and the run URL. The annotation carries the file, the line and the column.

Related guides

References

A blocked merge costs more than the run did. Latchkey runs the checks at $0.0025/min at 2 vCPU. Start free → 30-day trial · No credit card