Skip to content
Latchkey

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.

GitHub Actions
# caller step logs an empty value
echo "url="
# needs.deploy.outputs.url resolved to empty because deploy.yml
# never declared workflow_call.outputs.url

Common 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

  1. Set the value as a step output, then a job output inside the reusable workflow.
  2. Declare it under on.workflow_call.outputs mapping to that job output.
  3. Read it in the caller as needs.<callingjob>.outputs.<name>.
.github/workflows/deploy.yml
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.

.github/workflows/ci.yml
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.

Frequently asked questions

What causes "reusable "needs ... outputs" empty"?
A job output alone stays inside the reusable workflow. The caller only sees outputs declared under workflow_call.outputs.
How do I fix reusable "needs ... outputs" empty?
Declare and map the workflow_call output

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card