Skip to content
Latchkey

GitHub Actions Environment Secrets Empty - environment: Not Set on Job

A secret defined under a GitHub Environment resolves to empty because the job did not set environment:. Environment secrets and variables are only available to jobs explicitly assigned to that environment.

What this error means

A job reads secrets.<NAME> for a value stored as an environment secret and gets an empty string, while repo-level secrets work fine.

Actions log
Error: required secret DEPLOY_KEY is empty
# DEPLOY_KEY is an environment secret on "production",
# but the job has no environment: production

Diagnose it: what token do you actually have?

Permission failures in Actions are almost never about your repository settings alone. Three things combine: the default GITHUB_TOKEN permission set for the repo or organization, the permissions: block in the workflow, and whether the event is a fork pull request, which downgrades the token to read-only regardless of everything else.

.github/workflows/ci.yml
- name: Show the token scopes actually granted
  run: |
    curl -sI -H "Authorization: Bearer $GITHUB_TOKEN" \
      https://api.github.com/ | grep -i "^x-oauth-scopes\|^x-accepted"
    echo "event: ${{ github.event_name }}"
    echo "fork PR: ${{ github.event.pull_request.head.repo.fork }}"
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Common causes

Job missing environment:

Environment-scoped secrets and variables are injected only when the job declares environment:. Without it, those values are not in scope.

Secret stored at the wrong scope

A value saved as an environment secret is not visible as a repo secret, and vice versa. Mismatched scope yields an empty reference.

How to fix it

Assign the job to the environment

Set environment: on the job so its environment secrets and variables load.

.github/workflows/deploy.yml
jobs:
  deploy:
    environment: production
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}

Confirm the secret scope

  1. Check whether the secret is defined at repo, environment, or org level.
  2. Move it to the scope the job uses, or set environment: to match.
  3. For variables, the same rule applies via the vars context.

Grant the narrowest permission that works

Declaring a permissions: block switches the job from the repository default to exactly what you list, so an incomplete block is a common cause of a new failure right after someone tightened security. List every scope the job needs, not just the one that failed.

.github/workflows/ci.yml
permissions:
  contents: read        # checkout
  packages: write       # push to GHCR
  id-token: write       # OIDC to a cloud provider
  pull-requests: write  # comment on or label a PR
  checks: write         # publish check runs

How to prevent it

  • Set environment: on jobs that consume environment secrets.
  • Keep a single documented scope for each secret.
  • Verify the secret scope matches how the job is configured.

Frequently asked questions

What causes GitHub Actions environment secrets empty?
There are 2 common causes: job missing environment: and secret stored at the wrong scope. Environment-scoped secrets and variables are injected only when the job declares environment:.
How do I fix GitHub Actions environment secrets empty?
There are 2 fixes depending on which cause you have: assign the job to the environment and confirm the secret scope. Work through them in order, since the first is the most common.
What does GitHub Actions environment secrets empty actually mean?
A job reads secrets.<NAME> for a value stored as an environment secret and gets an empty string, while repo-level secrets work fine.
How do I stop GitHub Actions environment secrets empty happening again?
Set environment: on jobs that consume environment secrets. The prevention section lists 3 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