🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

GitHub Actions Composite Action Inputs Not Working: How to Fix It

Composite action inputs returning empty strings, secrets not passing through, or steps failing silently? Here are the exact fixes for common composite action bugs.

DevOpsBoys4 min read
Share:Tweet

Composite actions are one of GitHub Actions' most useful features for DRY workflows, but they have several sharp edges around inputs, secrets, and environment variables that trip up even experienced engineers.

The Most Common Issue: Inputs Not Being Received

yaml
# .github/actions/deploy/action.yml
name: Deploy to EKS
inputs:
  environment:
    required: true
  image_tag:
    required: true
 
runs:
  using: composite
  steps:
    - name: Deploy
      run: echo "Deploying ${{ inputs.image_tag }} to ${{ inputs.environment }}"
      shell: bash
yaml
# workflow.yml - calling the composite action
- uses: ./.github/actions/deploy
  with:
    environment: production
    image_tag: v1.2.3

If inputs.image_tag shows up empty, check these:

Fix 1: Missing shell in Composite Action Steps

This is the #1 composite action bug. Every run step in a composite action must specify shell — it's not optional like in regular workflows.

yaml
# WRONG - will silently fail or use wrong shell
runs:
  using: composite
  steps:
    - name: Deploy
      run: echo "Deploying ${{ inputs.image_tag }}"
      # Missing shell!
 
# CORRECT
runs:
  using: composite
  steps:
    - name: Deploy
      run: echo "Deploying ${{ inputs.image_tag }}"
      shell: bash   # REQUIRED in composite actions

Fix 2: Secrets Cannot Be Passed as Inputs

This is the most confusing composite action limitation: you cannot directly pass secrets to composite action inputs via with:.

yaml
# WRONG - this doesn't work as expected
- uses: ./.github/actions/deploy
  with:
    aws_secret_key: ${{ secrets.AWS_SECRET_KEY }}  # will be masked/empty

The correct approach is to pass secrets as environment variables:

yaml
# CORRECT - use env at the step or job level
- uses: ./.github/actions/deploy
  env:
    AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
    AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
  with:
    environment: production
    image_tag: v1.2.3

In your composite action, access them via ${{ env.AWS_SECRET_KEY }} or just $AWS_SECRET_KEY in shell.

Fix 3: Composite Action Can't Use secrets Context Directly

yaml
# WRONG - composite actions cannot use ${{ secrets.X }} directly
runs:
  using: composite
  steps:
    - run: aws s3 cp file.txt s3://bucket/
      env:
        AWS_SECRET_KEY: ${{ secrets.MY_SECRET }}  # This doesn't work in composite!
      shell: bash

Composite actions don't have access to the secrets context. The calling workflow must pass secrets through:

yaml
# In the composite action - receive via input
inputs:
  aws_secret_key:
    required: true
 
runs:
  using: composite
  steps:
    - run: echo "Key length: ${#AWS_SECRET_KEY}"
      env:
        AWS_SECRET_KEY: ${{ inputs.aws_secret_key }}
      shell: bash
yaml
# In the calling workflow - pass the secret
- uses: ./.github/actions/deploy
  with:
    aws_secret_key: ${{ secrets.AWS_SECRET_KEY }}  # Works as input from caller
    environment: production

Fix 4: Input Default Values Not Working

yaml
# This default value syntax is wrong
inputs:
  replicas:
    default: "3"
    description: "Number of replicas"
 
runs:
  using: composite
  steps:
    - run: kubectl scale deploy myapp --replicas=${{ inputs.replicas || 3 }}
      shell: bash

The || fallback in composite actions doesn't work as expected because inputs.replicas always has a value (the default). Use the input directly:

yaml
    - run: kubectl scale deploy myapp --replicas=${{ inputs.replicas }}
      shell: bash

If you need dynamic defaults:

yaml
    - name: Set replicas
      id: config
      run: |
        REPLICAS="${{ inputs.replicas }}"
        echo "replicas=${REPLICAS:-3}" >> $GITHUB_OUTPUT
      shell: bash
    
    - run: kubectl scale deploy myapp --replicas=${{ steps.config.outputs.replicas }}
      shell: bash

Fix 5: Outputs Not Being Set Correctly

yaml
# composite action outputs
outputs:
  image_digest:
    description: "The digest of the pushed image"
    value: ${{ steps.push.outputs.digest }}
 
runs:
  using: composite
  steps:
    - name: Build and push
      id: push
      run: |
        DIGEST=$(docker push myapp:latest | grep digest | awk '{print $3}')
        echo "digest=$DIGEST" >> $GITHUB_OUTPUT
      shell: bash

Common mistake: forgetting id: on the step that sets the output. Without id: push, ${{ steps.push.outputs.digest }} returns empty.

Fix 6: Composite Action Not Found

Error: Can't find 'action.yml', 'action.yaml' or 'Dockerfile' under '/home/runner/work/repo/.github/actions/my-action'.

For local composite actions, the path must be relative to the repo root:

yaml
# WRONG
- uses: .github/actions/deploy  # no leading dot-slash
 
# CORRECT  
- uses: ./.github/actions/deploy  # with dot-slash

Also verify the file is named exactly action.yml or action.yaml (not actions.yml).

Fix 7: if Conditions in Composite Steps

yaml
runs:
  using: composite
  steps:
    - name: Deploy to prod
      if: ${{ inputs.environment == 'production' }}  # WRONG syntax in composite
      run: ./deploy-prod.sh
      shell: bash
 
    # CORRECT - no ${{ }} around condition
    - name: Deploy to prod
      if: inputs.environment == 'production'
      run: ./deploy-prod.sh
      shell: bash

In composite action step conditions, don't wrap the expression in ${{ }}.

Debugging Composite Actions

yaml
# Add debug step to see all inputs
- name: Debug inputs
  run: |
    echo "environment: ${{ inputs.environment }}"
    echo "image_tag: ${{ inputs.image_tag }}"
    echo "ENV vars:"
    env | grep -i deploy
  shell: bash

Enable debug logging by setting secret ACTIONS_STEP_DEBUG=true in your repository.

Summary Checklist

  • Every run step has shell: bash (or shell: pwsh)
  • Secrets passed via env: not with:
  • Step that sets output has an id:
  • Local action path uses ./ prefix
  • if: conditions don't use ${{ }} wrapper
  • Inputs accessed with ${{ inputs.name }} not ${{ env.INPUT_NAME }}

Composite actions are worth the investment — a well-written deploy action can be reused across 20 workflows. Just watch out for these sharp edges.

Docs: GitHub Composite Actions

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments