GitHub Actions: "refusing to allow a GitHub App to create or update workflow"
GitHub refused to write a commit that adds or changes a file under .github/workflows, because the token has no right to write workflow files. Most of these come from scheduled jobs that keep a fork in step with its upstream, on the day the upstream edits one of its workflows.
What it looks like
From git push (promxy #1):
! [remote rejected] master -> master (refusing to allow a GitHub App to create or update workflow `.github/workflows/build.yml` without `workflows` permission)
From gh repo sync or the merge-upstream API (sparkDash #1):
gh: refusing to allow a GitHub App to create or update workflow `.github/workflows/publish-image.yml` without `workflows` permission (HTTP 422)
The words after "refusing to allow" name the kind of token:
- "a GitHub App":
GITHUB_TOKEN, or a GitHub App token without the Workflows permission. - "a Personal Access Token ... without `workflow` scope": a personal access token.
- "an OAuth App ... without `workflow` scope": a sign-in made through an OAuth app, such as
ghor a credential helper on your own machine.
When the check applies
GitHub's list of OAuth scopes says: "Workflow files can be committed without this scope if the same file (with both the same path and contents) exists on another branch in the same repository." What counts is the workflow files in the commits you push, not what you meant to change:
- A merge from upstream brings the upstream's workflow edits with it. A weekly sync in gcc-creative-studio #90 was refused after the upstream added a Postgres service to its
backend-tests.yml. - The same merge passes if the fork's own workflow files are put back before the commit. govee-homeassistant #7 merges with
--no-commit --no-ff, restores every file under.github/workflowsto the fork's copy, refuses to push if the merge would still change one, and its daily sync has passed on every run since, October 26 to November 2. - A push can pass by luck. In lilac-mono #90 one run went through only because another branch happened to carry the upstream's workflow files; once that branch was deleted, every run was refused. winmux #2 calls the refusal intermittent for the same reason.
GITHUB_TOKEN can never do it
workflows is not among the permissions a workflow can set for GITHUB_TOKEN, so no permissions block helps. The merge-upstream API that gh repo sync uses is checked the same way (github-code-monkey-bot #12).
What to do
- Push with a token that has the right:
- a GitHub App with the repository permissions Contents and Workflows set to read and write, its token made in the job by
actions/create-github-app-token; - a fine-grained personal access token with Contents and Workflows set to read and write, for that repository only;
- a classic personal access token with the
repoandworkflowscopes.
actions/checkoutastoken:. A token put only into the push URL can lose to the one the checkout saved, and the refusal still says "a GitHub App" ("Permission to ... denied to github-actions[bot]", 4). - a GitHub App with the repository permissions Contents and Workflows set to read and write, its token made in the job by
- Keep the fork's workflow files, as govee-homeassistant does above, when the fork has workflows of its own that the upstream does not have.
- Do not sync at all. sparkDash only needed the upstream's code to build an image, so it now checks out the upstream directly and dropped the sync.
Before you hand a sync job a stronger token, look at what it will push. promxy's job ran git reset --hard upstream/master and force-pushed; with a token that could write workflows, it would have deleted the fork's own mirror-sync.yml and stopped its own schedule after the first success. Every scheduled run had failed until then, and GitHub had disabled the workflow for inactivity.
On your own machine
- "a Personal Access Token": make a token with the
workflowscope (classic) or the Workflows permission (fine-grained), and replace the one your credential helper stored. - "an OAuth App": if
ghis your credential helper, add the scope with:gh auth refresh --scopes workflow
Telling it apart
remote: Permission to owner/repo.git denied to github-actions[bot]with a 403: the token could not write to the repository at all ("Permission to ... denied to github-actions[bot]").remote: error: GH006: Protected branch update failed: the token could write, but branch protection refused the push.
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: GitHub: a fork cannot be synced · "could not read Username for 'https://github.com'" · "Permission to ... denied to github-actions[bot]" · "Resource not accessible by integration" · "Invalid workflow file" · GitHub Actions plugins for IntelliJ IDEA compared · 日本語