DEV Community

Cover image for GitHub Actions unrecognized named-value: env and the context table
Kay
Kay

Posted on Originally published at latchkey.dev

GitHub Actions unrecognized named-value: env and the context table

GitHub Actions unrecognized named-value: env means the expression sits in a workflow key whose list of allowed contexts does not include env, most often runs-on, jobs.<job_id>.if, a job name, or the with: of a reusable workflow call, all of which are evaluated before a runner exists. The fix is to move the value, not reword the expression: use a vars variable, a job output read through needs, or a step-level if, which does accept env.

Workflow keys against the contexts each one accepts, with env and secrets marked absent
The rows people trip on. A job-level condition takes four contexts and four status functions, and neither env nor secrets is among them.

What this error means

The workflow is invalid and nothing runs. The message names the context in quotes, gives its position inside the expression, and prints the expression back to you, which makes the whole thing read like a typo even though the spelling is right. Two details give the real shape away. The same name works elsewhere in the same file, usually one key higher or one key lower, and the name that is rejected is almost always env or secrets. Both are contexts that exist, are documented, and are simply not permitted in the key where you used them. A long annotation with several of these separated by commas means the parser found every one of them in a single pass, so fixing the first line will not shorten the list.

```Invalid workflow file, quoted from actions/runner#480
The workflow is not valid. .github/workflows/dev.yml (Line: 15, Col: 11):
Unrecognized named-value: 'env'. Located at position 1 within expression: env.aws_account




## Common causes

### env used in a key that is evaluated before the runner exists

The largest group by far: `runs-on`, `jobs.<job_id>.if`, `jobs.<job_id>.name`, a job-level `concurrency`, or the `with:` of a called workflow. All of them are decided while the job is still being planned, and the environment belongs to a runner that has not been picked yet.

### secrets used in a condition

A job-level or step-level `if` does not accept the secrets context, by design rather than by omission. The documented replacement is to copy the secret into a job environment variable and test that, which also gives you a reliable way to ask whether a secret is set at all.

### matrix or strategy in a workflow-level key

The workflow-level keys, `run-name`, the top-level `env` and a workflow-level `concurrency`, are read before any job exists, so neither `matrix` nor `strategy` is on their lists. Inside a job the parser does not check whether a strategy is declared: `matrix.os` in the `runs-on` of a job without a matrix is accepted and resolves to an empty value. In our experience the rejected version arrives when a matrix value is promoted to a workflow-level `env` to share it between jobs.

### steps read from a job-level key

The `steps` context belongs to the job that is running and holds only steps "that have an `id` specified and have already run". A job-level `if`, a `runs-on` or a `with:` in a caller cannot see it, because at that point no step has run. Job outputs and the `needs` context are the way to move a value up a level.

## How to fix it

### Look up the key, not the expression

1. Take the workflow key from the line and column in the message, not the context name.
2. Find that key in the context availability table and read the list of contexts it accepts.
3. If the name you used is missing, move the value rather than rewriting the expression.

### Use a repository variable where you wanted a workflow env

The `vars` context is available in nearly every key in the table, including `runs-on`, a job-level `if` and a caller `with:`. A value that is configuration rather than a per-run computation belongs there, and moving it removes the whole class of problem rather than working around one instance.



```.github/workflows/ci.yml (illustrative)
jobs:
  build:
    runs-on: ${{ vars.RUNNER_LABEL }}
    steps:
      - run: echo building
Enter fullscreen mode Exit fullscreen mode

Compute it in a job and read it through needs

When the value really is computed at run time, produce it as a job output and read it from a later job. The needs context is available in every job-level key that matters, including runs-on, so this is the general answer for a dynamic runner label or a dynamic condition.

```.github/workflows/ci.yml (illustrative)
jobs:
plan:
runs-on: ubuntu-latest
outputs:
label: ${{ steps.pick.outputs.label }}
steps:
- id: pick
run: echo "label=ubuntu-latest" >> "$GITHUB_OUTPUT"

build:
needs: plan
runs-on: ${{ needs.plan.outputs.label }}
steps:
- run: echo building




### Push the condition down to the step

A step-level `if` accepts `env`, `steps`, `job` and `runner`, which is almost everything a job-level one refuses. If the only reason the condition was at job level was tidiness, moving it down is the smallest change that works, and it keeps the job green rather than skipped.

## How to prevent it

- Keep the context availability table open while writing any job-level expression.
- Put configuration in repository or organization variables, where `vars` is available almost everywhere.
- Pass secrets through `env:` at job or step level, never into a condition or a runner label.
- Lint workflows with a tool that knows the table, so the rejection happens on commit rather than on push.

## A minimal workflow that produces it

This file is written for this page and has never been run. It defines a workflow-level environment variable and then uses it in two keys that do not accept the `env` context. Both are rejected in one annotation, which is why a single mistake in this family often prints as several.



```.github/workflows/ci.yml (illustrative)
name: ci
on: [push]

env:
  runner_label: ubuntu-latest

jobs:
  build:
    name: build on ${{ env.runner_label }}
    runs-on: ${{ env.runner_label }}
    steps:
      - run: echo building
Enter fullscreen mode Exit fullscreen mode

The table that decides it

The contexts reference is unambiguous about what the table means: "The following table lists the restrictions on where each context and special function can be used within a workflow. The listed contexts are only available for the given workflow key, and may not be used anywhere else."

These are the rows that produce almost every report. Read the key you used in the left column, and if the name in your expression is not in the middle column, the parser will reject it no matter how the expression is written.

Workflow key Contexts available Special functions
env github, secrets, inputs, vars None
jobs.<job_id>.if github, needs, vars, inputs always, cancelled, success, failure
jobs.<job_id>.runs-on github, needs, strategy, matrix, vars, inputs None
jobs.<job_id>.env github, needs, strategy, matrix, vars, secrets, inputs None
jobs.<job_id>.with.<with_id> github, needs, strategy, matrix, inputs, vars None
jobs.<job_id>.steps.if github, needs, strategy, matrix, job, runner, env, vars, steps, inputs always, cancelled, success, failure, hashFiles
jobs.<job_id>.steps.run github, needs, strategy, matrix, job, runner, env, vars, secrets, steps, inputs hashFiles

Why env is missing from the interesting rows

A job-level condition is evaluated before the job is dispatched, and the environment does not exist yet at that point: it belongs to the runner that has not been chosen. The same reasoning covers runs-on, which is the key that chooses the runner in the first place. The step-level rows are different because by then a runner is running the job, which is why jobs.<job_id>.steps.if does accept env while jobs.<job_id>.if does not.

That is also why the same expression can work one line away from where it fails. actions/runner#2372 is the version of this that catches people moving to reusable workflows: a workflow-level env used in the with: of a called workflow, reported as "Unrecognized named-value: 'env'. Located at position 1 within expression". The variable is fine; the key does not take it.

Secrets in a condition, and the documented way round it

Secrets are a special case with an answer written into the workflow syntax reference: "Secrets cannot be directly referenced in if: conditionals. Instead, consider setting secrets as job-level environment variables, then referencing the environment variables to conditionally run steps in the job." The same passage adds the behavior you need for the check: "If a secret has not been set, the return value of an expression referencing the secret ... will be an empty string."

So the pattern is two steps. Lift the secret into the job environment, where the secrets context is available, then test the environment variable in a step-level condition, where the env context is available. The corrected workflow below does both, and it is the example the documentation itself uses.

```.github/workflows/ci.yml, corrected (illustrative)
jobs:
build:
runs-on: ubuntu-latest
env:
super_secret: ${{ secrets.SuperSecret }}
steps:
- if: ${{ env.super_secret != '' }}
run: echo 'the secret has a value'
- if: ${{ env.super_secret == '' }}
run: echo 'the secret is not set'




## Why there is no recorded run on this page

The parser rejects the file, so no job is ever created and there is no runner log to record. There is nothing transient here either: the same file fails the same way every time, so a retry changes nothing and no runner can repair it. The annotation quoted above comes from a public issue, and the workflows on this page are illustrative.

> When the token in quotes is punctuation rather than a context name, the message reads differently: [GitHub Actions unexpected symbol in a $&#123;&#123; &#125;&#125; expression](https://latchkey.dev/learn/github-actions/github-actions-unexpected-symbol-expression).

## FAQ

### What does Unrecognized named-value mean in GitHub Actions?

That the expression parser met a name it is not allowed to resolve in that position. The name usually exists as a context; it is just not on the list for the key you used. The message quotes the name, gives a one-based position inside the expression, and prints the expression, so the fix is to move the value rather than reword the expression.

### Why can I not use env in runs-on or a job-level if?

Because both are evaluated while the job is still being planned, before a runner exists to hold an environment. The context availability table lists `jobs.<job_id>.runs-on` as accepting `github`, `needs`, `strategy`, `matrix`, `vars` and `inputs`, and `jobs.<job_id>.if` as accepting `github`, `needs`, `vars` and `inputs`. Use `vars`, or a job output read through `needs`.

### Why are secrets not allowed in an if condition?

The workflow syntax reference states it and gives the replacement: "Secrets cannot be directly referenced in `if:` conditionals. Instead, consider setting secrets as job-level environment variables, then referencing the environment variables to conditionally run steps in the job." An unset secret returns an empty string, which is what makes the environment test reliable.

### Where do I check which contexts a workflow key accepts?

The context availability table in the contexts reference. It lists every key that has a restriction, the contexts allowed there, and the special functions allowed there. For each key it lists, the contexts named there are the complete set, and the table states that the listed contexts "may not be used anywhere else", which is the sentence the error comes down to.

## References

- [GitHub Actions: contexts reference, context availability](https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#context-availability)
- [GitHub Actions: workflow syntax, using secrets in a step condition](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idstepsif)
- [actions/runner#480: workflow level env does not work in all fields](https://github.com/actions/runner/issues/480)
- [actions/runner#2372: Unrecognized named-value env in a reusable workflow call](https://github.com/actions/runner/issues/2372)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)