GitHub Actions Environment Secrets Empty Without environment: on the Job
A secret defined at the environment level is empty in your job because the job did not declare environment:, so only repository and org secrets are available.
What this error means
secrets.MY_SECRET resolves to an empty string even though it is set under an environment. Moving the secret to repository scope, or adding environment: to the job, makes it appear.
jobs:
deploy:
runs-on: ubuntu-latest
# no environment: declared, so the "production" environment secret is empty
steps:
- run: echo "len=${#TOKEN}"
env:
TOKEN: ${{ secrets.PROD_TOKEN }}Common causes
Job did not declare the environment
Environment-scoped secrets only load for jobs that reference that environment. Without environment: production, the production secrets are not in scope.
Secret stored at the wrong scope
A secret meant to be broadly available was stored only on one environment, so jobs without that environment cannot read it.
How to fix it
Declare the environment on the job
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- run: ./deploy.sh
env:
TOKEN: ${{ secrets.PROD_TOKEN }}Choose the right secret scope
- Use environment secrets for values gated by environment protection.
- Use repository or org secrets for values every job needs.
- Avoid duplicating the same secret across scopes unless intentional.
How to prevent it
- Declare environment: on any job that reads environment-scoped secrets.
- Pick secret scope deliberately: environment vs repository vs org.
- Confirm a secret length in a debug step when wiring up a new deploy.