GitHub Actions reusable workflow outputs empty in needs.*.outputs in CI
To read a value from a called workflow, the reusable workflow must declare it under on.workflow_call.outputs and wire it to a job output. Without that declaration, needs.<job>.outputs.<name> in the caller is empty.
What this error means
A caller step that reads needs.deploy.outputs.url gets an empty string, or a downstream if: never fires. The reusable workflow produced the value internally but never exported it.
# caller step logs an empty value
echo "url="
# needs.deploy.outputs.url resolved to empty because deploy.yml
# never declared workflow_call.outputs.urlCommon causes
workflow_call.outputs was not declared
A job output alone stays inside the reusable workflow. The caller only sees outputs declared under workflow_call.outputs.
The output is not mapped to a job output
The workflow_call output must reference a specific job's output with the jobs.<id>.outputs expression, or it stays empty.
How to fix it
Declare and map the workflow_call output
- Set the value as a step output, then a job output inside the reusable workflow.
- Declare it under on.workflow_call.outputs mapping to that job output.
- Read it in the caller as needs.<callingjob>.outputs.<name>.
on:
workflow_call:
outputs:
url:
value: ${{ jobs.run.outputs.url }}
jobs:
run:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.dep.outputs.url }}
steps:
- id: dep
run: echo "url=https://app.example.com" >> "$GITHUB_OUTPUT"Consume the output in the caller
Reference the calling job in needs and read its output.
jobs:
deploy:
uses: ./.github/workflows/deploy.yml
notify:
needs: deploy
runs-on: ubuntu-latest
steps:
- run: echo "${{ needs.deploy.outputs.url }}"How to prevent it
- Always declare workflow_call.outputs for values the caller needs.
- Map each workflow_call output to a concrete jobs.<id>.outputs value.
- Add the calling job to needs before reading its outputs.