GitHub Actions composite action cannot access secrets directly
Composite actions cannot read the secrets context directly; only the calling workflow can. A composite action that references secrets.* gets an unrecognized context error or an empty value - secrets must be passed in as inputs.
What this error means
A composite action fails validation referencing secrets.*, or a secret it expected is empty at runtime.
Error: Unrecognized named-value: 'secrets'. Located at position 1 within expression: secrets.NPM_TOKENDiagnose it: what token do you actually have?
Permission failures in Actions are almost never about your repository settings alone. Three things combine: the default GITHUB_TOKEN permission set for the repo or organization, the permissions: block in the workflow, and whether the event is a fork pull request, which downgrades the token to read-only regardless of everything else.
- name: Show the token scopes actually granted
run: |
curl -sI -H "Authorization: Bearer $GITHUB_TOKEN" \
https://api.github.com/ | grep -i "^x-oauth-scopes\|^x-accepted"
echo "event: ${{ github.event_name }}"
echo "fork PR: ${{ github.event.pull_request.head.repo.fork }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}Common causes
Direct secrets reference inside composite
The composite action body references secrets.NPM_TOKEN, but the secrets context is not exposed to composite actions.
How to fix it
Pass the secret in as an input
- Declare an input on the composite action for the secret value.
- In the calling workflow, pass the secret via with.
- Reference inputs.<name> inside the composite action; re-run.
# action.yml (composite)
inputs:
npm-token:
required: true
runs:
using: composite
steps:
- run: npm publish
shell: bash
env:
NODE_AUTH_TOKEN: ${{ inputs.npm-token }}
# caller workflow
- uses: ./.github/actions/publish
with:
npm-token: ${{ secrets.NPM_TOKEN }}Grant the narrowest permission that works
Declaring a permissions: block switches the job from the repository default to exactly what you list, so an incomplete block is a common cause of a new failure right after someone tightened security. List every scope the job needs, not just the one that failed.
permissions:
contents: read # checkout
packages: write # push to GHCR
id-token: write # OIDC to a cloud provider
pull-requests: write # comment on or label a PR
checks: write # publish check runsHow to prevent it
- Pass secrets into composite actions as explicit inputs.
- Never reference the secrets context inside composite action bodies.