softprops/action-gh-release "Validation Failed: already_exists"
GitHub rejects the release create call with 422 "already_exists" when a release for the same tag is already published. The action tries to create rather than reuse.
What this error means
A release step fails with "Validation Failed" and a field error "already_exists" on tag_name.
Error: Validation Failed: {"resource":"Release","code":"already_exists","field":"tag_name"}
##[error]Validation FailedDiagnose 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
Tag already has a release
A prior run or a manual release created the same tag, so creating again is a conflict.
Re-running a published tag
Re-running the job on an existing tag attempts to recreate the release instead of updating it.
How to fix it
Tie the release to a fresh tag, or allow updates
- Trigger the release on push of a new tag (on: push: tags) so each release maps to a unique tag.
- Or move/delete the stale tag and release before re-running.
- Re-run; the create call now succeeds.
on:
push:
tags: ['v*']
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}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
- Drive releases from tag pushes so the tag is unique per release.
- Avoid re-running release jobs against an already-published tag.