How to fix "Permission denied to github-token" in GitHub Actions
My GitHub Actions workflow fails when trying to push a commit or tag:
remote: Permission to org/repo.git denied to github-actions[bot].
fatal: unable to access 'https://github.com/org/repo/': The requested URL returned error: 403
or when using gh CLI:
Error: Permission denied to github-token
The same workflow worked before or works on other repos. What is wrong?
Solution
By default, GitHub Actions restricts the GITHUB_TOKEN to read-only access for repository contents. Any write operation (pushing commits, creating releases, opening PRs) requires explicitly granting the permission.
Add a permissions block to your workflow file to grant the specific access you need:
name: Release
on:
push:
branches: [main]
permissions:
contents: write # allows pushing commits and creating releases
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Create release
run: gh release create v1.0.0 --generate-notes
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
You can also set permissions at the job level if only one job in the workflow needs write access.
Alternative #1
GitHub has a repository-level setting that controls the default permissions for GITHUB_TOKEN. If your organization or repository has it set to "Read repository contents and packages permissions", all new workflows start read-only.
To check or change it: go to your repository Settings, then Actions, then General, and look for "Workflow permissions". Switching it to "Read and write permissions" changes the default for all workflows in that repo, without needing to add permissions blocks to each file.
Be aware that granting broad write permissions to all workflows is less secure than using the permissions block to grant only what each workflow needs.
Alternative #2
If you need permissions that GITHUB_TOKEN cannot provide (for example, triggering another workflow, or accessing a different repository), use a Personal Access Token (PAT) stored as a repository secret:
- uses: actions/checkout@v4
with:
token: ${{ secrets.MY_PAT }}
- name: Push changes
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit -am "chore: automated update"
git push
env:
GITHUB_TOKEN: ${{ secrets.MY_PAT }}
Create the PAT under your GitHub account settings with the minimum required scopes (usually repo for private repos or public_repo for public ones) and add it as a secret in the repository settings.