Dev.to · 7 min read

GitHub Actions "Resource not accessible by integration": Fix It With the Smallest GITHUB_TOKEN Permission

GitHub Actions "Resource not accessible by integration": Fix It With the Smallest GITHUB_TOKEN Permission

The error means the GITHUB_TOKEN your job runs with lacks the permission for the API call it just made. Fix it by adding a permissions: block to the specific job with only the scope that call needs (for example pull-requests: write to comment on a PR). Don't flip the whole repository to "read and write" just to make the red X go away. What does the error actually look like? Depending on the action or CLI, you will see one of these in the job log: HttpError: Resource not accessible by integration RequestError [HttpError]: Resource not accessible by integration - https://docs.github.com/rest/... 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 gh: Resource not accessible by integration (HTTP 403) All of them come down to the same thing: GitHub authenticated the request as the workflow's app token, and that token's permission set did not include what the endpoint requires. It is an authorization problem, not a bad token, so regenerating secrets will not help. Takeaway: a 403 from github-actions[bot] is a missing permission scope, never an expired credential. Why did it work last month and fail now? Three things commonly change underneath you. Default token permissions. Each repository (and organization) has a setting under Settings → Actions → General → Workflow permissions. Newer repositories default to read-only for the token; older ones may still have the permissive default. A repo created or migrated after the default changed behaves differently from your old one. An explicit permissions: key. The moment a workflow or job declares any permission, every scope you did not list is set to none. Adding id-token: write for a cloud login quietly removes contents: read, and then actions/checkout fails on a private repo. Who triggered the run. Workflows triggered by a pull request from a fork get a read-only token, and by default no secrets. Runs triggered by Dependabot also get a read-only token by default. No permissions: block can widen that for a pull_request event from a fork. Takeaway: the same YAML can get different tokens depending on repo defaults, the trigger, and the PR author. Which permission does each step need? This is the part the error message never tells you. As of late 2026, these are the scopes I reach for most: What the step does Permission needed actions/checkout on a private repo contents: read Push a commit, create a tag or release contents: write Comment on or label a pull request pull-requests: write Create or comment on an issue issues: write Push an image to GitHub Container Registry packages: write Request an OIDC token for cloud login id-token: write Upload SARIF to code scanning security-events: write Deploy to GitHub Pages pages: write and id-token: write Write commit statuses or check runs statuses: write / checks: write One trap: PR comments go through the issues API under the hood, so on some endpoints issues: write also works, but pull-requests: write is the one that matches intent and is what most actions document. How do I scope it to one job? Set the default low at the top of the workflow, then raise it only in the job that needs it: name: pr-size-comment on: pull_request permissions: contents: read jobs: comment: runs-on: ubuntu-latest permissions: contents: read pull-requests: write steps: - uses: actions/checkout@v4 - name: Post a comment env: GH_TOKEN: ${{ github.token }} run: | gh pr comment "${{ github.event.pull_request.number }}" \ --body "Checked by CI." The workflow-level contents: read means any job you add later starts with the minimum. The job-level block replaces (not merges with) the workflow-level one, which is why contents: read is repeated there. Takeaway: declare read-only at the top, grant write per job, and repeat every scope the job still needs. Why does it still fail on fork pull requests? Because the restriction is deliberate. A fork's code is untrusted; if it could run with a write token or your secrets, anyone could open a PR that exfiltrates them. So a pull_request run from a fork cannot comment, label, or push, whatever your YAML says. In my testing, the confusing part is that the same workflow succeeds on branches inside the repo and fails only for outside contributors. Your options, from safest to riskiest: Split the work. Let the untrusted pull_request run build and test, save the results as an artifact, and have a second workflow triggered by workflow_run (which runs in the base repo's context) download the artifact and post the comment. This keeps untrusted code away from the write token. Skip the comment for forks. Guard the step with if: github.event.pull_request.head.repo.full_name == github.repository, so internal PRs get the comment and forks just pass quietly. pull_request_target. It runs with a write token against the base branch, but if you check out and execute the PR's code in that job you have handed the keys to the contributor. Use it only for steps that never run PR-supplied code. The same logic applies to Dependabot PRs: they get a read-only token and a separate Dependabot secrets store. If an auto-merge or comment workflow fails only on Dependabot branches, that is why. Takeaway: for fork PRs, do not fight the read-only token; move the privileged step into a trusted workflow_run job. What about reusable workflows and organization policy? Two more causes that look identical in the log: Reusable workflows can only reduce permissions. A called workflow inherits the caller's token and can downgrade it, but it cannot grant itself something the caller did not allow. Fix the permissions: on the calling job. Org-level restrictions. An organization owner can cap the default token to read-only and prevent repositories from overriding it. If your permissions: block looks correct and the call still returns 403, check the org's Actions settings before you debug the YAML. When a step needs more than the token can ever give, such as pushing to a different repository or triggering a workflow from a workflow (events created with GITHUB_TOKEN generally do not start new runs), use a GitHub App installation token or a fine-grained personal access token stored as a secret. A GitHub App is the better long-term choice because it is not tied to one person's account. The honest drawback: it adds setup (create the app, install it, mint a token in the job), so use it only when permissions: genuinely cannot cover the case. Takeaway: if the YAML is right and it still 403s, the ceiling is set by the calling workflow or the organization, not by your job. FAQ Why does GitHub Actions say "Resource not accessible by integration"? The GITHUB_TOKEN for that run does not have the permission scope the API endpoint requires. Add the matching scope, such as pull-requests: write, to the job's permissions: block. Is it safe to set workflow permissions to "read and write" for all workflows? It works, but it gives every job, including ones that run third-party actions, write access it does not need. Keep the repository default read-only and grant write per job. Why can't my workflow comment on pull requests from forks? Fork PRs run with a read-only GITHUB_TOKEN and no secrets by design. Post the comment from a separate workflow_run workflow instead. Bottom line If you own the repo and the failing job is a trusted internal one, add a job-level permissions: block with the single scope from the table and stop there. If the failure only happens for forks or Dependabot, restructure so the privileged step runs through workflow_run rather than loosening the token. If you need cross-repository writes or chained workflow triggers, move to a GitHub App token. Treat "read and write for everything" as a last resort, not a fix. Related reading Preview Environments Per Pull Request: How to Seed Them Without Cloning Production ChatGPT Free vs Go vs Plus vs Pro: Which Tier Should a Coding Beginner Actually Pay For? When Should You Move Off Managed Hosting to Kubernetes? The Thresholds That Actually Matter

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News