GitHub Actions: "Permission to ... denied to github-actions[bot]"
A git push in a workflow was refused. github-actions[bot] is the name GitHub shows for GITHUB_TOKEN, so the push went out with the job's own token, and that token could not write to the repository named in the message.
What it looks like
remote: Permission to grafana/grafana.git denied to github-actions[bot]. fatal: unable to access 'https://github.com/grafana/grafana/': The requested URL returned error: 403 ##[error]The process '/usr/bin/git' failed with exit code 128
This is from a weekly job in grafana #136263. Read two names in the first line: the repository after "Permission to" is where the push went, and the account after "denied to" is who git signed in as. If you meant to push with a token of your own and it still says github-actions[bot], go to 4.
1. The token was read only
- The job asked for less. The grafana job declares
permissions: contents: read, while its last step,peter-evans/create-pull-request, has to push a branch. GitHub's workflow syntax reference: "If you specify the access for any of these permissions, all of those that are not specified are set tonone." Adding a workflow-levelcontents: readtook the push away from procrastinate's coverage job, and only runs on main pushed, so the checks on pull requests stayed green (procrastinate #1610). - The default is read only. Under Settings, Actions, General, "Workflow permissions" is either read and write for everything or read only for
contentsandpackages, and an organization can force the second. A docs deploy with nopermissionsblock failed this way in gemma #11.
Grant contents: write to the job that pushes, not to the whole workflow. The proposed fix for gemma builds the docs in one job and pushes gh-pages from a second, which is the only job with write access and runs no project code:
jobs:
deploy:
needs: generate
permissions:
contents: write
2. The run gets a read-only token whatever the workflow says
- A
pull_requestfrom a fork. From the workflow syntax reference: you "typically can't grant write access" withpermissionsfor forks, unless an admin turned on "Send write tokens to workflows from pull requests", which only private repositories offer. In lazypredict #579 a docs preview pushed togh-pagesfor every pull request, so every one from a fork had a red check; the fix described there is to run the preview only when the branch lives in the repository:if: github.event.pull_request.head.repo.full_name == github.repository
- A run started by Dependabot from
push,pull_request,pull_request_revieworpull_request_review_comment. GitHub treats it like a fork: a read-only token, and only Dependabot secrets. Unlike forks,permissionscan raise the token here. A secret missing from the Dependabot secrets is empty in these runs, so a step that falls back togithub.tokenpushes as the bot: in carbon_theme #77 the push token was in neither store and the job hadcontents: read, and the fix gave that jobcontents: write.
3. The push went to another repository
GitHub's documentation: "The token's permissions are limited to the repository that contains your workflow." No permissions line changes that. Compare the name after "Permission to" with your repository:
- A docs site in a separate
owner.github.iorepository (endless #552). - A branch on a contributor's fork. HttpArena pushes saved benchmark results back to the pull request's branch with a token that has
contents: write; for a fork owned by an organization the push was refused, because only forks owned by a user can allow edits from maintainers. The step ended in|| echo "Warning: ...", so the job passed and the pull request was merged without the results (HttpArena #1702). What a push to a fork's branch needs: GitHub: you cannot push to a pull request branch in a fork.
For another repository, use a GitHub App token (actions/create-github-app-token) or a fine-grained personal access token with write access to that repository, stored as a secret. Then check 4, because the checkout may still send its own token.
4. Your token was there, but the checkout's went out
actions/checkout saves its token in the local git config unless persist-credentials: false is set, and that saved header wins over a token written into the push URL. Three merged fixes from the last two months:
- openai-ads-toolkit #16: pushing to mirror repositories with a URL that carried
SPLIT_TOKEN, every push went out as the bot. - hriv #1645: a Dependabot workflow minted an app token for its push, and the push authenticated as the read-only
GITHUB_TOKEN. - endless #552: the docs deploy broke after a checkout upgrade from v2 to v7. Since v6.0.0 checkout keeps the credential in a separate file, pulled in by
includeIfentries that also cover.git/worktrees/*, and the deploy action pushes from a worktree.
Either stop the checkout from saving its token, or hand your token to the checkout so the saved one is yours:
- uses: actions/checkout@v7
with:
persist-credentials: false
# or
- uses: actions/checkout@v7
with:
token: ${{ secrets.DEPLOY_TOKEN }}
Telling it apart
remote: error: GH006: Protected branch update failed: the token could write, but branch protection refused the push, for example a formatting job pushing straight to amainthat only takes pull requests (udistribution #198).refusing to allow a GitHub App to create or update workflow ... without `workflows` permission: the push changes a file under.github/workflows.workflowsis not among the permissions a workflow can giveGITHUB_TOKEN, so this needs an App with that permission or a personal access token with the workflow scope (spikeforge-dashboard #61; more in "refusing to allow a GitHub App to create or update workflow").- The same missing permission on an API call shows up as "Resource not accessible by integration".
- A push with
GITHUB_TOKENthat works but starts no workflow is by design: events it causes "will not create a new workflow run", apart fromworkflow_dispatch,repository_dispatchand somepull_requestevents.
See failed jobs from IntelliJ IDEA
Pipeline Inbox for GitHub Actions Workflows, a plugin by Lesstep for JetBrains IDEs 2024.1 or later, lists the runs of all your repositories in an IDE tool window, with their jobs and steps, and your current branch's runs are in the status bar. Reading is free; re-running failed jobs, cancelling, running workflows and reviewing deployments are Pro (US$1.50 a month, with a 30-day trial).
GitHub and GitHub Actions are trademarks of GitHub, Inc.; IntelliJ IDEA is a trademark of JetBrains s.r.o. This page is not affiliated with, sponsored or endorsed by GitHub, Inc. or JetBrains.
More: "could not read Username for 'https://github.com'" · "Resource not accessible by integration" · "API rate limit exceeded for installation" · "Input required and not supplied" · GitHub Actions plugins for IntelliJ IDEA compared · 日本語