774 words
4 minutes

GitHub Actions Reusable Workflow Secrets Not Passed: A Practical Fix

2026-08-10
DevOps
GitHub Actions
/
CI/CD
/
DevOps
/
Troubleshooting

If a secret is available in the caller workflow but empty in a reusable workflow, GitHub Actions did not pass it across the workflow boundary. Declare the secret contract in the called workflow, map the value directly or use secrets: inherit where it is allowed, and pass it again at every nested boundary.

Declare the secret in the reusable workflow#

A reusable workflow is called through workflow_call. Named secrets make the contract visible and let GitHub validate the caller’s input:

.github/workflows/reusable-deploy.yml
name: Reusable deploy
on:
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
deploy_token:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy
env:
DEPLOY_TOKEN: ${{ secrets.deploy_token }}
DEPLOY_ENVIRONMENT: ${{ inputs.environment }}
run: ./scripts/deploy.sh "$DEPLOY_ENVIRONMENT"

The caller passes a named secret on the job that uses the reusable workflow:

.github/workflows/deploy.yml
jobs:
deploy:
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: staging
secrets:
deploy_token: ${{ secrets.DEPLOY_TOKEN }}

The uses form is a job-level call, so the reusable workflow’s inputs and secrets belong beside with, secrets, and uses, not inside a step. The caller and called workflow also need compatible permissions; a called workflow cannot elevate the permissions it receives.

Choose direct mapping or secrets: inherit#

Use direct mapping when the reusable workflow needs a small, documented set of secrets:

secrets:
deploy_token: ${{ secrets.DEPLOY_TOKEN }}
package_token: ${{ secrets.PACKAGE_TOKEN }}

Use secrets: inherit when the caller and reusable workflow are inside the same organization or enterprise boundary and passing the available repository, organization, or enterprise secrets is intentional:

jobs:
deploy:
uses: org/platform/.github/workflows/reusable-deploy.yml@main
secrets: inherit

inherit is not a general cross-repository secret tunnel. For a reusable workflow outside the supported organization or enterprise boundary, map the required secrets explicitly or keep the secret-consuming job in the caller repository. Document the expected secret names even when using inherit; otherwise a rename can look like a permissions problem.

Forward secrets through nested reusable workflows#

Secrets do not automatically travel through a chain. If workflow A calls workflow B and B calls workflow C, A must pass a secret to B and B must pass it to C:

# A: caller
jobs:
release:
uses: ./.github/workflows/b.yml
secrets:
deploy_token: ${{ secrets.DEPLOY_TOKEN }}
# B: reusable workflow calling C
jobs:
publish:
uses: ./.github/workflows/c.yml
secrets:
deploy_token: ${{ secrets.deploy_token }}

If B receives the secret but C does not, inspect the B-to-C edge first. The GitHub reusable workflow documentation also limits how far a nested chain can extend and requires secrets to be passed directly through each link.

Check environment-secret precedence#

Environment secrets are a common source of a misleading “missing secret” result. Secrets defined under an environment cannot be passed through on.workflow_call. If a job in the called workflow declares an environment, a secret with the same name from that environment takes precedence over the secret the caller attempted to pass.

Decide which boundary owns the credential:

  • Use a workflow_call secret for a value supplied by the caller.
  • Use an environment secret when deployment approval and environment protection should own the value.
  • Do not give both boundaries the same name unless the precedence is deliberate and documented.

Debug the mapping without printing the value#

Check presence, not the secret itself. A temporary diagnostic step can expose whether the called workflow received a value while keeping the value masked:

- name: Check deploy token wiring
env:
DEPLOY_TOKEN: ${{ secrets.deploy_token }}
run: |
if [ -z "$DEPLOY_TOKEN" ]; then
echo '::error::deploy_token was not passed to the reusable workflow'
exit 1
fi

Remove the diagnostic after fixing the contract. Never print a secret, its length, or a transformed version of it as a debugging shortcut.

A quick checklist#

  1. The called file uses on.workflow_call and names the required inputs and secrets.
  2. The caller maps each secret under the job-level secrets key, or intentionally uses secrets: inherit.
  3. Every nested caller forwards the secret again.
  4. No called job environment silently replaces the caller’s value.
  5. The called workflow’s permissions are sufficient for the operation.
  6. Diagnostics test presence without exposing the credential.

If the same caller also uses typed manual inputs, see how GitHub Actions handles boolean workflow-dispatch inputs. For a later artifact failure, check the hidden-file behavior of upload-artifact separately; it is a different boundary from secret passing.

FAQ#

Q: Does secrets: inherit work for any public reusable workflow?#

A: No. inherit is intended for supported organization or enterprise boundaries. Use explicit mapping when the caller and called workflow do not share the required boundary.

Q: Why does a nested reusable workflow receive an empty secret?#

A: The intermediate workflow probably received the secret but did not pass it in its own secrets mapping. Each A-to-B-to-C edge needs an explicit handoff.

Q: Can on.workflow_call receive an environment secret?#

A: No. Environment secrets are not passed through the workflow_call interface. If the called job selects an environment, its environment secret can take precedence over a caller-provided secret with the same name.

References:

GitHub Docs: Reuse workflows

GitHub Docs: Workflow syntax

GitHub Docs: Use secrets in workflows

GitHub Actions Reusable Workflow Secrets Not Passed: A Practical Fix
https://laplusda.com/en/posts/github-actions-reusable-workflow-secrets-not-passed/
Author
Zero
Published at
2026-08-10
License
CC BY-NC-SA 4.0
Was this article useful?

Report a typo or broken link, or suggest a related topic.