GitHub Actions: "The template is not valid"
The run started, and the runner then evaluated an expression whose result it could not use. The file itself passed validation, which is why it reads as correct. The line and column point at the expression, in the workflow or in an action's action.yml. Where it stops tells you which part: at Set up job, in two or three seconds, it is the job's own keys (services:, container: and the like); at a step, it is that step's env:, with:, if: or continue-on-error:.
What it looks like
The template is not valid. .github/workflows/release.yml (Line: 172, Col: 27): Error reading JToken from JsonReader. Path '', line 0, position 0.
The template is not valid. .github/workflows/build.yml (Line: 185, Col: 21): Unexpected value ''
The template is not valid. System.TimeoutException: hashFiles('code-index.db.gz') couldn't finish within 120 seconds.
1. "Error reading JToken from JsonReader": fromJSON on an empty string
fromJSON('') fails, so an output that was never set breaks the expression. The usual case now is release-please: its pr output is empty on a run that opens or updates no release pull request, so fromJSON(steps.release.outputs.pr).headBranchName fails after each release merge (compose-preview-mcp #151), and in pulumi-provider-git on 6 of 10 runs (#54).
An if: on the same step does not protect it. The runner evaluates a step's env: before its if:, and fails the step when that evaluation fails (StepsRunner.cs). It does this for later steps even after an earlier step failed, so in envoyproxy/examples this error was printed on top of the real one (#1526). Pass the raw text and parse it in the shell, which several of these projects did:
- if: steps.release.outputs.pr != ''
env:
RELEASE_PR: ${{ steps.release.outputs.pr }}
run: |
branch=$(jq -r .headBranchName <<< "$RELEASE_PR")
echo "branch=$branch" >> "$GITHUB_OUTPUT"
Or give fromJSON something to read: fromJSON(steps.release.outputs.pr || '{}'). That covers an empty value only. Text that is not JSON, such as a free-text workflow_dispatch input, still fails (gh-aw #64249).
2. "Unexpected value ''": an empty value where one is required
- Docker Hub
credentials:onservices:orcontainer:. The workflow schema typesusernameandpasswordas non-empty strings (workflow-v1.0.json). Pull requests from forks get no secrets, and Dependabot gets only Dependabot secrets, so on those runs${{ secrets.DOCKERHUB_USERNAME }}is empty and the job fails at Set up job before any step. eshu (#7948), goiabada (#577), curia (#2158) and village-os (#462) all had comments saying the runner would pull anonymously without the secrets. curia and goiabada made each block one expression that gives an empty mapping when the secrets are missing; an empty mapping passes, and the image is then pulled without a login:credentials: ${{ secrets.DOCKERHUB_USERNAME && secrets.DOCKERHUB_TOKEN && fromJSON(format('{{"username":{0},"password":{1}}}', toJSON(secrets.DOCKERHUB_USERNAME), toJSON(secrets.DOCKERHUB_TOKEN))) || fromJSON('{}') }}village-os moved its services tomirror.gcr.ioinstead, which needs no credentials. Either way those runs pull anonymously, and count against Docker Hub's limit for the address: Docker Hub's pull limit in GitHub Actions. continue-on-error: ${{ matrix.ignore-error }}when some matrix entries have no such key: the value is'', notfalse. ruby/digest saw it on top of a failing test step (#162).${{ matrix.ignore-error || false }}gives a boolean either way.
3. hashFiles: "couldn't finish within 120 seconds"
hashFiles() has a fixed 120-second limit (HashFilesFunction.cs), and a ** pattern walks the whole workspace, including node_modules and anything the job cloned or built. In code-search-index the if: of one step timed out, and the publish step after it was skipped too (#52). Name the file without ** when you know where it is (hashFiles('package-lock.json')). To test whether a file exists, a step that writes an output does not walk the workspace at all; code-search-index replaced its hashFiles conditions with one:
- id: files
if: always()
run: |
if [ -f code-index.db ]; then echo "db=true"; else echo "db=false"; fi >> "$GITHUB_OUTPUT"
- if: steps.files.outputs.db == 'true'
run: ./publish.sh
4. hashFiles: "An error occurred trying to start process '…/externals/node20/bin/node'"
hashFiles() runs on the runner's own copy of Node.js. Up to runner 2.337 that was node20, so a runner installed without it fails at every hashFiles(), usually in an actions/cache key: a minimal image on RunsOn (runner-images-for-aws #78) and a runner packaged in Nix (consortium-autoresearch #49). The 2.338.0 release notes: "The runner's own Node usage (hashFiles(), ./run.sh --check, and the service wrapper runsvc.sh) now uses Node 24 instead of Node 20". Updating the runner to 2.338.0 or later fixes it.
Telling it apart
- The run has no jobs and says "Invalid workflow file": the file itself was refused before anything ran ("Invalid workflow file").
- "Unrecognized named-value" with "Failed to load" and an
action.yml: a context the action's metadata cannot use ("Unrecognized named-value" in action.yml). - "Input required and not supplied": the expression worked and gave an empty input to an action that needs one ("Input required and not supplied").
See failed jobs from IntelliJ IDEA
Pipeline Inbox for GitHub Actions Workflows, a plugin by Lesstep for JetBrains IDEs 2024.1 or later, lists the runs of all your repositories in an IDE tool window, with their jobs and steps, and your current branch's runs are in the status bar. Reading is free; re-running failed jobs, cancelling, running workflows and reviewing deployments are Pro (US$1.50 a month, with a 30-day trial).
GitHub and GitHub Actions are trademarks of GitHub, Inc.; IntelliJ IDEA is a trademark of JetBrains s.r.o. This page is not affiliated with, sponsored or endorsed by GitHub, Inc. or JetBrains.
More: "Invalid workflow file" · "Unable to process file command" · Docker Hub's pull limit · GitHub Actions plugins for IntelliJ IDEA compared · 日本語