GitHub Actions needs.<job>.outputs is null because the upstream job was skipped
needs.<job>.outputs only carries values when the upstream job actually ran and set them. A skipped upstream job (failed if-condition, path filter) leaves its outputs unset, so the consumer reads empty strings.
What this error means
A downstream step receives an empty value from needs.<job>.outputs.<name> even though the output is defined.
jobs:
build:
if: github.ref == 'refs/heads/main' # skipped on feature branches
outputs:
tag: ${{ steps.meta.outputs.tag }}
deploy:
needs: build
steps:
- run: echo "tag is '${{ needs.build.outputs.tag }}'" # prints '' when build skippedCommon causes
Upstream job skipped by its if-condition
A skipped job runs no steps, so it produces no outputs and downstream reads are empty.
Path or event filter skipped the producer
paths/paths-ignore or event filters can skip the producer while the consumer still runs.
How to fix it
Guard the consumer on the producer result
- Add an if that checks needs.<job>.result == ’success’ before using its outputs.
- Provide a default value when the upstream is skipped.
deploy:
needs: build
if: ${{ needs.build.result == 'success' }}
steps:
- run: echo "${{ needs.build.outputs.tag }}"Always run the producer and branch inside it
- Remove the job-level if and instead branch within the job so it always sets outputs.
- Have the job emit a sentinel output when work is skipped.
How to prevent it
- Treat needs outputs as optional whenever the producer has an if-condition.
- Check needs.<job>.result before consuming its outputs.