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.

Last modified: April 16, 2026
Stay in the loop
Subscribe to our newsletter to get the latest articles delivered to your inbox