Fix: GitHub Actions Reusable Workflow Not Working — Inputs Not Passed or Secrets Not Available
Quick Answer
How to fix GitHub Actions reusable workflow issues — workflow_call trigger, passing inputs and secrets, output variables, caller vs called permissions, and common errors.
The Problem
A reusable workflow doesn’t receive inputs from the caller:
# caller.yml
jobs:
deploy:
uses: ./.github/workflows/deploy.yml
with:
environment: production # Input not available in called workflowOr secrets aren’t passed to the reusable workflow:
# In called workflow — secret is empty
- run: echo "Token is ${{ secrets.API_TOKEN }}"
# Output: Token isOr the called workflow can’t access outputs from the reusable workflow:
# Caller — output is empty
- run: echo "Version is ${{ needs.build.outputs.version }}"
# Output: Version isOr the workflow fails with:
Error: Unrecognized named-value: 'inputs'
Error: Required input 'environment' is not providedWhy This Happens
Reusable workflows use the workflow_call trigger and have strict rules for passing data:
workflow_calltrigger required — the called workflow must declareon: workflow_call:to accept calls from other workflows. Without it, the workflow can’t be reused.- Inputs must be declared — the called workflow must explicitly declare every input under
on.workflow_call.inputs. Passing undeclared inputs is silently ignored. - Secrets must be passed explicitly — secrets from the caller are not automatically available in called workflows. They must be explicitly passed via
secrets:in the caller and declared in the called workflow. Or usesecrets: inheritto pass all secrets automatically. inputscontext only available in called workflows — using${{ inputs.foo }}in a regular workflow (not called viaworkflow_call) causes “Unrecognized named-value: ‘inputs’”.
Fix 1: Define workflow_call Correctly
The called workflow must declare on: workflow_call: with all inputs and secrets:
# .github/workflows/deploy.yml — REUSABLE workflow
name: Deploy
on:
workflow_call:
# Declare all inputs this workflow accepts
inputs:
environment:
required: true
type: string
description: 'Target environment (staging, production)'
docker_tag:
required: false
type: string
default: 'latest'
description: 'Docker image tag to deploy'
dry_run:
required: false
type: boolean
default: false
# Declare secrets this workflow needs
secrets:
DEPLOY_TOKEN:
required: true
description: 'Token for deployment API'
SLACK_WEBHOOK:
required: false
# Declare outputs this workflow produces
outputs:
deployment_id:
description: 'ID of the created deployment'
value: ${{ jobs.deploy.outputs.deployment_id }}
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
deployment_id: ${{ steps.create-deployment.outputs.deployment_id }}
steps:
- name: Deploy to ${{ inputs.environment }}
id: create-deployment
if: ${{ !inputs.dry_run }}
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: |
DEPLOYMENT_ID=$(./scripts/deploy.sh \
--env ${{ inputs.environment }} \
--tag ${{ inputs.docker_tag }})
echo "deployment_id=$DEPLOYMENT_ID" >> $GITHUB_OUTPUT
- name: Dry run notification
if: ${{ inputs.dry_run }}
run: echo "DRY RUN: would deploy ${{ inputs.docker_tag }} to ${{ inputs.environment }}"
- name: Notify Slack
if: ${{ secrets.SLACK_WEBHOOK != '' }}
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
-d '{"text": "Deployed to ${{ inputs.environment }}"}'Fix 2: Call a Reusable Workflow with Inputs and Secrets
# .github/workflows/release.yml — CALLER workflow
name: Release
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
outputs:
docker_tag: ${{ steps.tag.outputs.tag }}
steps:
- uses: actions/checkout@v4
- name: Generate tag
id: tag
run: echo "tag=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
deploy-staging:
needs: build
uses: ./.github/workflows/deploy.yml # Relative path for same repo
with:
environment: staging
docker_tag: ${{ needs.build.outputs.docker_tag }}
secrets:
DEPLOY_TOKEN: ${{ secrets.STAGING_DEPLOY_TOKEN }}
# SLACK_WEBHOOK: not passed — optional secret, caller omits it
deploy-production:
needs: [build, deploy-staging]
uses: ./.github/workflows/deploy.yml
with:
environment: production
docker_tag: ${{ needs.build.outputs.docker_tag }}
secrets:
DEPLOY_TOKEN: ${{ secrets.PROD_DEPLOY_TOKEN }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
# Use outputs from the reusable workflow
post-deploy:
needs: deploy-production
runs-on: ubuntu-latest
steps:
- name: Log deployment ID
run: echo "Deployed with ID ${{ needs.deploy-production.outputs.deployment_id }}"Using secrets: inherit (simpler):
# Pass ALL secrets from caller to called workflow automatically
deploy-production:
uses: ./.github/workflows/deploy.yml
with:
environment: production
secrets: inherit # All secrets from the caller are availableNote:
secrets: inheritpasses all secrets the caller has access to. The called workflow must still declare which secrets it uses underon.workflow_call.secrets— but they don’t needrequired: trueto be available.
Fix 3: Reference Reusable Workflows in Other Repos
Call workflows from other repositories:
# Call a workflow from another repository
jobs:
lint:
uses: myorg/shared-workflows/.github/workflows/lint.yml@main
# Format: {owner}/{repo}/.github/workflows/{file}@{ref}
with:
node_version: '20'
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# Use a specific commit SHA for reproducible builds
deploy:
uses: myorg/shared-workflows/.github/workflows/deploy.yml@abc1234
with:
environment: production
# Use a release tag
test:
uses: myorg/shared-workflows/.github/workflows/[email protected]Calling across organizations requires explicit permissions:
# The called workflow's repository must allow it
# Settings → Actions → General → Allow {org} to use reusable workflows
# Or use a PAT with repo scope as a secretFix 4: Pass Matrix Values to Reusable Workflows
Combine matrix strategy with reusable workflows:
# Caller — matrix of environments
jobs:
deploy-matrix:
strategy:
matrix:
environment: [dev, staging, production]
include:
- environment: production
requires_approval: true
- environment: staging
requires_approval: false
- environment: dev
requires_approval: false
uses: ./.github/workflows/deploy.yml
with:
environment: ${{ matrix.environment }}
secrets: inheritNote: You can’t pass matrix values directly to the
uses:path — only towith:andsecrets:. The workflow path itself must be a static string.
Fix 5: Reusable Workflow Patterns
Build-once, deploy-many pattern:
# .github/workflows/ci-cd.yml
jobs:
test:
uses: ./.github/workflows/test.yml
secrets: inherit
build:
needs: test
uses: ./.github/workflows/build.yml
with:
push_image: true
secrets: inherit
# outputs: image_tag
deploy-staging:
needs: build
uses: ./.github/workflows/deploy.yml
with:
environment: staging
image_tag: ${{ needs.build.outputs.image_tag }}
secrets: inherit
integration-tests:
needs: deploy-staging
uses: ./.github/workflows/integration-test.yml
with:
target_url: https://staging.example.com
secrets: inherit
deploy-production:
needs: [build, integration-tests]
uses: ./.github/workflows/deploy.yml
with:
environment: production
image_tag: ${{ needs.build.outputs.image_tag }}
secrets: inheritAdding environment protection rules:
# deploy.yml — require approval for production
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }} # Links to GitHub Environment settings
# production environment has protection rules (required reviewers)
steps:
- name: Deploy
run: ./deploy.shFix 6: Debug Reusable Workflow Issues
# Add debug output to trace inputs and secrets
jobs:
debug:
runs-on: ubuntu-latest
steps:
- name: Print inputs
run: |
echo "environment: ${{ inputs.environment }}"
echo "docker_tag: ${{ inputs.docker_tag }}"
echo "dry_run: ${{ inputs.dry_run }}"
- name: Check secret availability
run: |
if [ -z "$SECRET_VALUE" ]; then
echo "DEPLOY_TOKEN is empty!"
else
echo "DEPLOY_TOKEN is set (length: ${#SECRET_VALUE})"
fi
env:
SECRET_VALUE: ${{ secrets.DEPLOY_TOKEN }}Common error messages and fixes:
Error: "Unrecognized named-value: 'inputs'"
→ The workflow isn't called via workflow_call — inputs context unavailable
→ Add 'on: workflow_call:' or check the trigger type
Error: "Required input 'environment' is not provided"
→ Caller uses `with:` but doesn't include all required inputs
→ Add the missing input in the caller's `with:` block
Error: "Unexpected value 'uses'"
→ 'uses' at job level requires no 'steps' — jobs with 'uses' can't have steps
→ Split into two separate jobs
Warning: "Secret 'API_TOKEN' was not passed to the called workflow"
→ The called workflow declares the secret but caller doesn't pass it
→ Add 'secrets: { API_TOKEN: ${{ secrets.API_TOKEN }} }' to the callerStill Not Working?
Reusable workflow job can’t have steps — a job that calls a reusable workflow with uses: cannot also have steps:. If you need both, use two separate jobs with needs:.
env context not available in called workflows — environment variables set in the caller are not passed to called workflows. Pass them as explicit inputs instead.
Outputs from reusable workflows need needs: reference — to use outputs from a reusable workflow job, the consumer job must declare it in needs: and reference it as ${{ needs.<job-id>.outputs.<output-name> }}. The output must be declared both at the step level (echo "foo=bar" >> $GITHUB_OUTPUT), job level (outputs: foo: ${{ steps.step-id.outputs.foo }}), and workflow_call level (outputs: foo: value: ${{ jobs.job-id.outputs.foo }}).
For related GitHub Actions issues, see Fix: GitHub Actions Cache Not Working and Fix: GitHub Actions Timeout.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: GitHub Actions Job Timeout — Workflow Cancelled or Stuck After 6 Hours
How to fix GitHub Actions timeout issues — job-level and step-level timeouts, stuck processes, self-hosted runner timeouts, debugging hanging jobs, and timeout best practices.
Fix: GitHub Actions Artifacts Not Working — Upload Fails, Download Empty, or Artifact Not Found
How to fix GitHub Actions artifact issues — upload-artifact path patterns, download-artifact across jobs, retention days, artifact name conflicts, and the v3 to v4 migration.
Fix: GitHub Actions Secret Not Available — Environment Variable Empty in Workflow
How to fix GitHub Actions secrets that appear empty or undefined in workflows — secret scope, fork PR restrictions, environment protection rules, secret names, and OIDC alternatives.
Fix: GitHub Actions Matrix Strategy Not Working — Jobs Not Running or Failing
How to fix GitHub Actions matrix strategy issues — matrix expansion, include/exclude patterns, failing fast, matrix variable access, and dependent jobs with matrix outputs.