External Publication
Visit Post

GitHub Actions' required properties aren't always required

Jamie Tanna February 18, 2026
Source

Earlier today on Renovate we noticed that our documentation site had not been deploying for a couple of days.

The root cause was down to a quirk of GitHub Actions, where a required property isn't actually always enforced to be required 😅

To explain this, let's look at the following composite action:

# .github/actions/setup-node/action.yml
name: 'Setup Node and install dependencies'
description: 'Setup Node and install dependencies using cache'
inputs:
  node-version:
    description: 'Node version'
    required: true
  os:
    description: 'Composite actions do not support `runner.os`, so it must be passed in as an input'
    required: true
  save-cache:
    description: 'Save cache when needed'
    required: false
    default: 'false'
runs:
  using: 'composite'
  steps:
    - name: Calculate `CACHE_KEY`
      shell: bash
      run: |
        echo 'CACHE_KEY=node_modules-${{
          inputs.os
        }}-${{
          inputs.node-version
        }}-${{
          hashFiles('pnpm-lock.yaml', 'package.json')
        }}' >> "$GITHUB_ENV"

     # ...

This is then used as part of our build:

# .github/workflows/build.yml
  build-deploy-docs-site:
    needs: release

    permissions: # ...

    environment: # ...

    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          show-progress: false

      - name: Setup Node.js
        uses: ./.github/actions/setup-node
        with:
          node-version: ${{ needs.setup-build.outputs.node-version }}
          os: ${{ runner.os }}

Can you spot what's wrong here?

You may notice this line:

          node-version: ${{ needs.setup-build.outputs.node-version }}

We're referencing a job, but that wasn't found in our needs definition.

In this case, GitHub Actions is therefore evaluating this statement as the empty string, resolving as node-version: "".

Because there is technically something passed to the node-version argument, required: true isn't flagging any issues.

In my opinion, required: true should error if it's receiving empty input, but maybe this is instead a slightly painful gotcha, but one I'm now going to going to remember going forwards.

We've since added a check in our composite action to fail the build if either required: true options are unset.

It looks like actionlint would have caught this too - so that's on my TODO list to add to our CI pipelines now, too!

Discussion in the ATmosphere

Loading comments...