semantic-release push to protected branch rejected in CI
When semantic-release pushes the release commit or tag to a protected branch, GitHub rejects it if required status checks or reviews are not satisfied. The automatic GITHUB_TOKEN cannot bypass branch protection, so the push fails even with contents write.
What this error means
The push during release fails with "protected branch hook declined" or "required status check ... is expected", and the release commit never lands.
remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Changes must be made through a pull request. Required status check "ci" is expected.
! [remote rejected] main -> main (protected branch hook declined)Common causes
GITHUB_TOKEN cannot bypass protection rules
Branch protection applies to the automatic token; it has no bypass permission, so direct pushes to the protected branch are declined.
Required reviews or checks block direct pushes
The rule requires a pull request or passing checks, which a direct release push cannot satisfy.
How to fix it
Push with a GitHub App or PAT that can bypass
- Create a GitHub App (or PAT) allowed to bypass branch protection.
- Add it to the branch protection bypass list.
- Pass its token to semantic-release as GITHUB_TOKEN or GH_TOKEN.
- uses: actions/create-github-app-token@v1
id: apptoken
with:
app-id: ${{ vars.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- run: npx semantic-release
env:
GITHUB_TOKEN: ${{ steps.apptoken.outputs.token }}Move version bumps out of the protected branch
Configure branches so the release commit does not need to push to a protected branch, or avoid @semantic-release/git committing back.
How to prevent it
- Use a GitHub App token on the branch-protection bypass list.
- Remember GITHUB_TOKEN never bypasses branch protection.
- Keep release automation principals in the protection bypass allowlist.