Skip to content
Latchkey

secrets: inherit Not Working in Reusable Workflows - Fix

A called (reusable) workflow reads a secret as empty because the caller did not forward it. Reusable workflows do not see the caller's secrets automatically - each must be passed in the secrets: block or via secrets: inherit.

What this error means

A reusable workflow's step gets an empty secret value, so an API call or deploy fails, even though the secret exists in the calling repo. The caller never forwarded it to the called workflow.

.github/workflows/ci.yml
# caller - secrets not forwarded
jobs:
  build:
    uses: ./.github/workflows/deploy.yml
    # missing: secrets: inherit  (or an explicit secrets: map)
# in deploy.yml, secrets.DEPLOY_TOKEN is empty

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

Caller did not pass secrets

A reusable workflow only receives secrets the caller explicitly forwards. Omitting both the secrets: map and secrets: inherit leaves them undefined in the called workflow.

Called workflow did not declare the secret

When forwarding explicitly (not inherit), the called workflow must declare each secret under on.workflow_call.secrets, or it will not be available.

How to fix it

Forward secrets explicitly or inherit

Pass the needed secrets by name, or use secrets: inherit to forward all of the caller's secrets.

.github/workflows/ci.yml
# caller
jobs:
  deploy:
    uses: ./.github/workflows/deploy.yml
    secrets:
      DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
    # or, to pass everything:
    # secrets: inherit

Declare secrets in the called workflow

  1. Add each forwarded secret under on.workflow_call.secrets in the called workflow.
  2. Use secrets: inherit only when the caller is trusted to expose all its secrets to the called workflow.
  3. For nested reusable workflows, forward at every level - inheritance is not transitive unless each level inherits.

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

  • Forward reusable-workflow secrets explicitly, or use secrets: inherit deliberately.
  • Declare on.workflow_call.secrets in the called workflow.
  • Forward at each nesting level; inheritance is not automatic deep down.

Frequently asked questions

What causes secrets: inherit not working in reusable workflows?
There are 2 common causes: caller did not pass secrets and called workflow did not declare the secret. A reusable workflow only receives secrets the caller explicitly forwards.
How do I fix secrets: inherit not working in reusable workflows?
There are 2 fixes depending on which cause you have: forward secrets explicitly or inherit and declare secrets in the called workflow. Work through them in order, since the first is the most common.
What does secrets: inherit not working in reusable workflows actually mean?
A reusable workflow's step gets an empty secret value, so an API call or deploy fails, even though the secret exists in the calling repo.
How do I stop secrets: inherit not working in reusable workflows happening again?
Forward reusable-workflow secrets explicitly, or use secrets: inherit deliberately. 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