Skip to main content
ISSUE-423LowPipeline Composition

Workflow file is not valid

Control:CTRL-423Workflow files must be valid

📋 What is this?

A file under .github/workflows cannot be parsed as a workflow: an undefined YAML anchor, a duplicate key, a tab-indented block, a syntax error. GitHub shows such a file as an invalid workflow on the Actions tab and does not run it.

  • One finding per file, with the parser's own message (unknown anchor 'checkout_with' referenced, line 7: mapping key "runs-on" already defined).
  • The other workflow files of the repository are analyzed and scored as usual. When every file is invalid there is nothing left to analyze: the controls that read the workflows show as not evaluated rather than passed, and only this finding and repository-level controls such as branch protection report a result. A file that could not be read or fetched is reported separately, as a collection problem, not as this finding.
  • An empty file, or a file with no jobs: block, is not flagged, although GitHub may still refuse it.
  • Only a YAML parse failure is reported. A file that is well-formed YAML but that GitHub still rejects is not flagged: a job without runs-on, an unterminated ${{ }} expression, a needs: naming a job that does not exist. The Actions tab, or a linter such as actionlint, reports those.

⚠️ Impact

Whatever that workflow was meant to do, a test suite, a security scan, a release gate, never happens, and nothing else in the repository notices: the file sits in .github/workflows looking like coverage it does not provide.

🔧 How to fix

Open the Actions tab and read the error GitHub reports for the file, then fix it. For an undefined anchor, define it in the same file before using it or inline the block: GitHub Actions resolves YAML anchors within one file only, and never merge keys. Confirm the workflow appears without the invalid-workflow banner once fixed.

✗ BeforePlumber reports "unknown anchor 'checkout_with' referenced"; GitHub refuses the file and neither job ever runs.
# .github/workflows/anchor-aliases.yml: ❌ the alias has no anchor in this file
name: Anchor Aliases
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
with: *checkout_with
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
with: *checkout_with
✓ AfterThe same anchor, defined once in the file and reused by alias.
# .github/workflows/anchor-aliases.yml: ✅ the anchor is defined before it is used
name: Anchor Aliases
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
with: &checkout_with
persist-credentials: false
fetch-depth: 0
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7
with: *checkout_with

💡 Tips

  • A duplicate key (on: twice, or runs-on: twice in one job) is refused too, even though many YAML parsers silently keep the last value. Keys are compared ignoring case, as GitHub does: on: next to ON:, or FOO next to foo under env:, is a duplicate.
  • A YAML merge key (<<: *anchor) is not flagged: Plumber's parser resolves it, but GitHub Actions does not, so such a file can pass this control and still be refused by GitHub.
  • With the control turned off, or absent from a full configuration without extends, the control shows as skipped and an invalid file degrades the run instead: the report says it could not be parsed and the score is withheld until it is fixed or the control is enabled.

⚙️ Configure this control

With the CLI alone: set it in your configuration file. Every field and default is in the controls configuration reference, CTRL-423.