できない.dev

GitHub Actions の GITHUB_TOKEN で push / API ができない(permission denied・403)

GitHub Actions の GITHUB_TOKEN は既定で読み取りのみに絞られている。
git push で Write access to repository not granted.(または Permission to ... denied)と 403、API 呼び出しで 403 Resource not accessible by integration になる場合、ワークフローの permissions: ブロックとリポジトリ設定の Workflow permissions を見直す。

公開: 更新:

実行例あり(2026-09-21 に実環境で検証)

要約

ワークフロー内で自動付与される GITHUB_TOKEN は、リポジトリ設定で 読み取り専用 に絞られているのが既定。git push が remote: Write access to repository not granted. のあと The requested URL returned error: 403 で拒否される場合(Permission to <owner>/<repo>.git denied to github-actions[bot] と出ることもある)も、REST / GraphQL API のステップが 403 Resource not accessible by integration で落ちる場合も、原因は同じで トークンに対象操作の権限が無い ことである。
ワークフロー側の permissions: 宣言とリポジトリの Workflow permissions を組み合わせて、必要なスコープだけを明示的に許可する。

実行例

GitHub の ubuntu-latest ランナーで job の permissions を contents: read だけにすると、ジョブ開始時の GITHUB_TOKEN Permissions 欄には Contents: read と Metadata: read しか並ばず、git push は「Write access to repository not granted.」のあと HTTP 403 で拒否される。
同じトークンで issue を作ろうとした REST API 呼び出しも 403 の Resource not accessible by integration で失敗し、リポジトリ情報の permissions はすべて false と返ってきた。

GITHUB_TOKEN Permissions
Contents: read
Metadata: read
$ git push origin HEAD:refs/heads/lab-token-permission-denied
remote: Write access to repository not granted.
fatal: unable to access 'https://github.com/camo-maybe/dekinai-com/': The requested URL returned error: 403
$ gh api -X POST "repos/$GITHUB_REPOSITORY/issues" -f title='lab probe (should fail)'
{"message":"Resource not accessible by integration","documentation_url":"https://docs.github.com/rest/issues/issues#create-an-issue","status":"403"}gh: Resource not accessible by integration (HTTP 403)
$ gh api "repos/$GITHUB_REPOSITORY" --jq '.permissions'
{"admin":false,"maintain":false,"pull":false,"push":false,"triage":false}

— 2026-09-21 時点の出力

検証環境

検証日
実行環境
github-actions-runner (ubuntu-latest)Ubuntu 24.04.5 LTS
バージョン
  • Runner 2.337.0
  • Runner Image ubuntu-24.04 20260907.300.1
  • Git 2.55.0
  • actions/checkout v7

この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。

よくある原因

  1. 既定が read-only: 2023 年以降に作成されたリポジトリは Workflow permissions の既定が Read で、書き込みは明示しないと拒否される。
    Organization 側で既定を固定していることもある。
  2. permissions: 未指定: ワークフロー / ジョブで permissions: を書いていないとリポジトリ既定が適用される。
    書き込みが必要な処理でも自動で許可されない。
  3. スコープ不足: contents: write は付けたが、Packages への push(packages: write)や Pages のデプロイ(pages: write + id-token: write)など、対象機能のスコープを付け忘れている。
  4. Fork からの PR: セキュリティ上、Fork 由来の pull_request 実行では GITHUB_TOKEN が読み取り専用に制限され、permissions: を書いても書き込みにはならない。
  5. クロスリポジトリ push: GITHUB_TOKEN は発行元リポジトリにしか書き込めない。
    別リポジトリへ push したい場合は別の認証手段が必要。
  6. ブランチ保護: main の direct push 禁止やレビュー必須が設定されていると、書き込み権があっても push 自体が拒否される。

解決策

1. ワークフローで必要権限を明示

トップレベルに書くと全 job に適用される。
最小権限の原則で必要分だけ書く。

permissions:
  contents: write
  pull-requests: write
 
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git push

job 単位で絞ることもできる。
書き込みが要る job だけに付けると、他の job は read-only のまま保てる。

jobs:
  release:
    permissions:
      contents: write
      packages: write

automatic-token-authentication(新しいタブで開く) に許可可能スコープの一覧がある。

2. リポジトリ設定を Read and write に切り替え

Settings → Actions → General → Workflow permissions を Read and write permissions に変更する。
ワークフロー側の permissions: の方が常に優先されるので、リポジトリ設定はベースラインの引き上げ用と考える。
Organization で固定されている場合は管理者に依頼する。

3. 対象機能のスコープを足す

403 Resource not accessible by integration は「操作しようとした対象のスコープが無い」という意味で、contents: write だけでは足りないことが多い。

permissions:
  contents: read
  packages: write      # ghcr.io へ push
  pages: write         # GitHub Pages のデプロイ
  id-token: write      # actions/deploy-pages が要求
  issues: write        # issue へのコメント・ラベル

4. Fork からの PR

Fork 由来の PR では書き込みトークンが渡らない。
ラベル付与やコメントなど書き込みが必要な処理は、PR の実行そのものから切り離し、マージ後の push イベントやメンテナが起動する workflow_dispatch で動かす。

5. 別リポジトリには PAT / App トークン

別リポジトリへ push するなら、Fine-grained PAT か GitHub App の installation token を Secrets に登録し actions/checkout の token: に渡す。

- uses: actions/checkout@v4
  with:
    repository: owner/other-repo
    token: ${{ secrets.CROSS_REPO_PAT }}

6. ブランチ保護の確認

main への直接 push を禁止していると、書き込み権があっても拒否される。
CI 経由の push が必要なら Bypass list に github-actions[bot] を追加するか、PR ベースのフローに切り替える。

この記事は役立ちましたか?