GitHub Actions env at Workflow vs Job vs Step - Wrong Value Wins
An env variable holds an unexpected value because env is defined at multiple levels. Step env overrides job env, which overrides workflow env - the most specific definition wins for that step.
What this error means
A step reads an env var that is different from what the workflow-level env block sets, because a job- or step-level env block of the same name shadows it.
env:
NODE_ENV: production # workflow level
jobs:
test:
env:
NODE_ENV: test # job level overrides workflow
steps:
- run: echo "$NODE_ENV" # prints "test", not "production"Common causes
Same env name defined at multiple levels
GitHub merges env from workflow, job, and step scopes. When the same key exists at more than one level, the more specific scope wins for that step.
Assuming workflow env is global and final
A workflow-level env block is a default, not a lock. A job or step can redefine the key, so the workflow value is not guaranteed downstream.
How to fix it
Define each variable at one intended scope
Put shared defaults at the workflow level and only override deliberately at job or step level.
env:
REGION: us-east-1 # default for all jobs
jobs:
deploy:
steps:
- run: ./deploy.sh
env:
REGION: eu-west-1 # intentional per-step overrideTrace which scope set the value
- Echo the variable in the failing step to see the effective value.
- Search the workflow for every env: block defining that key.
- Remove accidental redefinitions; keep only the scope you intend to win.
How to prevent it
- Keep each env key defined at a single intended scope.
- Treat workflow-level env as defaults that jobs may override on purpose.
- Echo effective env values when debugging precedence.