GitHub Actions Reusable Workflow Environment Variables
Résumé
Pixel shares why environment variables do not inherit across GitHub Actions reusable workflow calls. The announcement explains why caller job env blocks and env expressions fail, distinguishes reusable workflows from composite actions, and points to inputs, secrets, and organization variables as supported ways to pass values.
DEV Community
GitHub Actions reusable workflow env not inherited by the called file
GitHub Actions reusable workflow env not inherited is documented behavior rather than a bug, and the restriction is stricter than most people expect: the calling job cannot even declare an env block, because the schema shape for a job with uses has no such key. Values cross that boundary as inputs, as secrets or as organization variables, and as nothing else. Three things cross the call and two do not. The two that do not are the two people reach for first. What this error means The called workflow reads a variable the caller set and gets an empty string. The same env block works perfectly for the caller's own jobs, which is what makes this read as a scoping subtlety rather than a hard boundary. If you then try the obvious repairs, they fail in two different ways: adding env to the calling job is rejected when the file is parsed, and passing the value through with using an env expression is rejected as an unrecognized named-value. Neither message mentions reusable workflows. ```Quoted from the title of actions/runner#2372 Unrecognized named-value: 'env'. Located at position 1 within expression
Common causes
Expecting workflow-level env to propagate
The commonest cause and the most reasonable expectation, because env does reach every job and every step inside the file that declares it. The boundary is the call, not the file, and the documentation lists the behavior among the limitations of reusable workflows rather than among the rules for env, which is why it is easy to miss.
Treating a called workflow like a composite action
A composite action runs inside the calling job and shares its environment, so habits formed there transfer badly. A called workflow runs as its own jobs, on its own runners, with a fresh environment, so there is no shared process for a variable to survive in.
Passing the value with an env expression
Reaching for with: key: ${{ env.VALUE }} on the calling job. The context list for that key does not include env, so this fails as an unrecognized named-value rather than as an empty string, and the message names the context instead of the boundary.
Computing the value in a step of the caller
Writing to $GITHUB_ENV in one job and expecting a later calling job to see it. Environment changes do not cross jobs at all, reusable workflow or not, and the call is a job rather than a step, so there is nowhere for a step-scoped value to be read from.
How to fix it
Declare an input and pass the value explicitly
Give the reusable workflow a typed input and pass it from the caller. This is the documented mechanism, it is readable at the call site, and it makes the contract between the two files visible to anyone editing either one.
```.github/workflows/build.yml and ci.yml (illustrative)
build.yml
on:
workflow_call:
inputs:
app_env:
type: string
required: true
ci.yml
jobs:
call:
uses: ./.github/workflows/build.yml
with:
app_env: staging
Use a variable for anything shared across workflows Repository and organization variables are readable in both files without being passed, which removes the plumbing entirely. The documentation makes the same recommendation for reusing values across workflows, and it is the right answer for things like an environment name, a region or a registry host. ```.github/workflows/build.yml (illustrative) steps: - run: ./deploy.sh --env "${{ vars.APP_ENV }}"
Publish a computed value as a job output first
- Compute the value in an ordinary job in the caller and write it to
$GITHUB_OUTPUT. - Map it to a job output with an
outputsblock on that job. - Add
needson the calling job so theneedscontext resolves. - Pass it through
with, readingneeds.<job>.outputs.<name>, which is a context that key accepts.
```.github/workflows/ci.yml (illustrative)
call:
needs: prepare
uses: ./.github/workflows/build.yml
with:
app_env: ${{ needs.prepare.outputs.app_env }...

Powered by Readybot.io