Skip to content
Latchkey

GitHub Actions Composite Action Outputs Empty in the Caller

A composite action runs fine but its output is empty in the calling workflow. Composite outputs must be declared under outputs: in action.yml and bound to an inner step’s output via the steps context.

What this error means

A workflow reads steps.<action-id>.outputs.<name> after calling a local composite action and gets an empty string, even though the inner step wrote to GITHUB_OUTPUT.

.github/workflows/ci.yml
# caller
- id: setup
  uses: ./.github/actions/setup
- run: echo "${{ steps.setup.outputs.version }}"  # empty

Common causes

Output not declared in action.yml

A composite action only exposes outputs listed under outputs:. Writing to GITHUB_OUTPUT inside a step is not enough; the action must map it out.

Wrong binding expression

The output value must reference the inner step via the steps context, e.g. value: ${{ steps.<inner>.outputs.<name> }}, with an inner step id that matches.

How to fix it

Declare and bind the output in action.yml

Map each composite output from the inner step that produced it.

action.yml
# .github/actions/setup/action.yml
outputs:
  version:
    value: ${{ steps.v.outputs.version }}
runs:
  using: composite
  steps:
    - id: v
      shell: bash
      run: echo "version=1.2.3" >> "$GITHUB_OUTPUT"

Verify the names line up end to end

  1. Confirm the inner step id in the outputs binding matches the producing step.
  2. Confirm the output key written to GITHUB_OUTPUT matches the declared name.
  3. Read it in the caller via the action’s step id: steps.<action-id>.outputs.<name>.

How to prevent it

  • Always declare composite outputs under outputs: with a steps-context binding.
  • Keep inner step ids and output names consistent.
  • Echo the value inside the action to confirm it is non-empty before mapping.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →