Skip to content
Latchkey

GitHub Actions "The expression is too long (max 21000 characters)"

Every individual GitHub Actions expression is parsed into an AST and is capped at 21000 characters. A giant inlined fromJSON blob, a long concatenated string, or a deeply nested toJSON usually trips it.

What this error means

The workflow fails at parse time on a single key that contains one large \${{ }} expression, most often a matrix built from an inlined JSON literal or a long interpolated run command.

github-actions
Invalid workflow file: .github/workflows/ci.yml#L22
The expression is too long. Max allowed length: 21000

Diagnose 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.

.github/workflows/ci.yml
- 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 itContexts available there
run-namegithub, inputs, vars
concurrencygithub, inputs, vars
Top-level envgithub, secrets, inputs, vars
jobs.<id>.ifgithub, needs, vars, inputs
jobs.<id>.steps.ifgithub, needs, strategy, matrix, job, runner, env, vars, steps, inputs
jobs.<id>.outputsFull access, including secrets
Reusable workflow outputsgithub, jobs, vars, inputs

Common causes

Huge inline JSON passed to fromJSON

A matrix or value built from a large JSON literal embedded directly in the YAML inflates a single expression past the ceiling.

Long concatenated/interpolated string

Building a big command or payload with many \${{ }} concatenations inside one expression hits the limit.

How to fix it

Move the large value out of the expression

  1. Generate the JSON in a prior step and pass it through GITHUB_OUTPUT, then reference the output once.
  2. Split a long interpolation into multiple steps or multiple smaller expressions.
  3. For matrices, produce the array in a setup job and consume it with fromJSON of a single short output.
.github/workflows/ci.yml
jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      items: ${{ steps.gen.outputs.items }}
    steps:
      - id: gen
        run: echo "items=$(cat matrix.json)" >> "${GITHUB_OUTPUT}"
  build:
    needs: setup
    strategy:
      matrix:
        item: ${{ fromJSON(needs.setup.outputs.items) }}

Catch 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.

Terminal
# 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 -color

How to prevent it

  • Keep generated data in step outputs or files, not inlined into one expression.
  • Reference outputs by name instead of re-building large strings inline.

Frequently asked questions

What causes GitHub Actions "The expression is too long (max 21000 characters)"?
There are 2 common causes: huge inline json passed to fromjson and long concatenated/interpolated string. A matrix or value built from a large JSON literal embedded directly in the YAML inflates a single expression past the ceiling.
How do I fix GitHub Actions "The expression is too long (max 21000 characters)"?
Move the large value out of the expression. Generate the JSON in a prior step and pass it through GITHUB_OUTPUT, then reference the output once.
What does GitHub Actions "The expression is too long (max 21000 characters)" actually mean?
The workflow fails at parse time on a single key that contains one large \${{ }} expression, most often a matrix built from an inlined JSON literal or a long interpolated run command.
How do I stop GitHub Actions "The expression is too long (max 21000 characters)" happening again?
Keep generated data in step outputs or files, not inlined into one expression. The prevention section lists 2 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card