GitHub Actions "Matrix must define ..." - Fix Matrix Syntax Errors
The strategy.matrix block is malformed: it has no values, the include/exclude entries are the wrong shape, or a step references a matrix key that is not part of every combination.
What this error means
The workflow fails to compile with a matrix error, or jobs expand into unexpected combinations. An empty or mistyped matrix is rejected before any job starts.
Error: Matrix vector 'node' does not contain any values
# or
Error: The workflow is not valid. Matrix must define at least one vectorDiagnose it: print the context before you change anything
Most workflow-expression bugs are not syntax errors, they are an expression reading something that is empty. GitHub resolves a missing property to an empty string instead of failing the run, so a wrong reference looks like a logic bug rather than a mistake. Dump the contexts first and you will usually see the answer immediately.
- name: Dump contexts
run: |
echo '--- github ---' ; echo '${{ toJSON(github) }}'
echo '--- needs ---' ; echo '${{ toJSON(needs) }}'
echo '--- steps ---' ; echo '${{ toJSON(steps) }}'
echo '--- matrix ---' ; echo '${{ toJSON(matrix) }}'
echo '--- inputs ---' ; echo '${{ toJSON(inputs) }}'Check the context is allowed where you used it
Contexts are not available everywhere. The same expression can be valid in a step if and invalid in a job if, which is why an expression that works in one workflow fails when moved.
| Where you wrote it | Contexts available there |
|---|---|
run-name | github, inputs, vars |
concurrency | github, inputs, vars |
Top-level env | github, secrets, inputs, vars |
jobs.<id>.if | github, needs, vars, inputs |
jobs.<id>.steps.if | github, needs, strategy, matrix, job, runner, env, vars, steps, inputs |
jobs.<id>.outputs | Full access, including secrets |
Reusable workflow outputs | github, jobs, vars, inputs |
Common causes
Empty or non-list matrix dimension
Each matrix key must map to a non-empty list. A key set to null, an empty list, or a scalar produces "does not contain any values".
Malformed include or exclude
include and exclude must be lists of objects whose keys match matrix dimensions. A flat list or mismatched keys is rejected or silently adds unintended combinations.
Referencing a key absent from some combinations
If a matrix key only exists via include, combinations without it leave matrix.<key> empty, breaking steps that assume it is always set.
How to fix it
Define each dimension as a non-empty list
strategy:
matrix:
node: [18, 20, 22]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}Use include/exclude with matching keys
Add or remove specific combinations with objects whose keys align to the matrix dimensions.
strategy:
matrix:
node: [18, 20]
os: [ubuntu-latest]
include:
- node: 22
os: macos-latest
exclude:
- node: 18
os: ubuntu-latestCatch it before it reaches CI
Every failure in this cluster is statically detectable. actionlint parses workflow expressions, checks context availability against the same rules above, and validates needs references, so these bugs never need to cost you a run.
# one-off
docker run --rm -v "$(pwd):/repo" --workdir /repo rhysd/actionlint:latest -color
# as a job, before anything expensive runs
- uses: actions/checkout@v4
- run: |
bash <(curl -s https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
./actionlint -colorHow to prevent it
- Validate matrix shape with actionlint before pushing.
- Keep every dimension a non-empty list and align include/exclude keys.
- Add fail-fast: false during debugging so one bad combination does not cancel the rest.