Fix GitHub Actions "GITHUB_TOKEN Permission Denied" Errors

Topic: github-actions-permission-denied-tokenUpdated 8/3/2026

Quick Answer

  • Root cause: GITHUB_TOKEN defaults to read-only permissions in GitHub Actions, so any write operation (pushing commits, publishing packages, creating releases) fails with permission errors.
  • First check: Look at your workflow's job-level permissions: block. If it's missing, the token defaults to read-only for most scopes.
  • Minimal fix: Add permissions: contents: write (or the specific scope you need) to the job that requires write access.
  • Environment boundary: This applies to all GitHub-hosted and self-hosted runners using the automatically-generated GITHUB_TOKEN. For cross-repository operations, you must use a PAT or GitHub App token instead.

What Problem It Solves

GitHub Actions automatically injects a GITHUB_TOKEN secret into every workflow run. This token authenticates actions and scripts as the github-actions[bot] user. However, GitHub deliberately restricts this token to read-only permissions by default as a security measure.

When your workflow attempts to:

  • Push commits back to the repository
  • Create or edit releases
  • Publish packages to GitHub Packages
  • Modify issues or pull requests
  • Delete or rename files

...the token lacks the required write scope, and the workflow fails with errors like:

remote: Permission to user/repo.git denied to github-actions[bot].
npm ERR! code E403: You do not have permission to publish "package-name".
Resource not accessible by integration

The solution is to explicitly declare the permissions your job needs using the permissions: key.

When This Error or Setup Appears

Permission-denied errors surface in several common scenarios:

ScenarioTypical Error
Auto-commit generated files (build artifacts, updated lockfiles)remote: Permission to user/repo.git denied
Publishing to GitHub Packages (npm, Docker, Maven)npm ERR! code E403
Creating releases or tagsResource not accessible by integration
Using third-party actions that write to the repoError: Input required and not supplied: token
Cross-repository operationsResource not accessible by integration

The error appears at the point where the write operation executes, not at workflow startup. This makes debugging confusing because earlier steps may succeed before the failure occurs.

Minimal Working Configuration

Add a permissions: block to the job that needs write access:

YAML
name: Auto-commit build output

on:
  push:
    branches: [main]

jobs:
  build-and-commit:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: npm run build

      - name: Commit changes
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add dist/
          git commit -m "Update build output"
          git push

For publishing packages:

YAML
jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Publish to GitHub Packages
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Parameters and Environment Variables

The permissions: key accepts these scopes at both workflow and job level:

ScopeGrants access to
actionsWorkflow management (cancel, approve, etc.)
checksCheck runs and check suites
contentsRepository contents (push, delete, edit files)
deploymentsDeployments
discussionsRepository discussions
issuesIssues and issue comments
packagesGitHub Packages
pagesGitHub Pages
pull-requestsPull requests and PR comments
security-eventsCode scanning alerts
statusesCommit statuses

Each scope accepts read, write, or none:

YAML
permissions:
  contents: write
  packages: read
  issues: none

You can also set a default at the workflow level and override per job:

YAML
permissions:
  contents: read

jobs:
  build:
    permissions:
      contents: write  # overrides workflow default for this job

The GITHUB_TOKEN itself is automatically available as ${{ secrets.GITHUB_TOKEN }} — you do not need to create or configure it.

Root Cause Analysis

GitHub introduced default read-only permissions for GITHUB_TOKEN in February 2023. Before this change, the token had broad write access by default. The change was a security hardening measure: most workflows only need read access, and granting write by default created unnecessary risk if a workflow was compromised.

The token's permissions are determined by:

  1. Workflow-level permissions: block — sets defaults for all jobs
  2. Job-level permissions: block — overrides workflow defaults for that job
  3. If neither is specified — the default is read-only for all scopes (except actions and packages, which default to none)

The token is scoped to the current repository only. It cannot access other repositories, even within the same organization. This is why cross-repository operations fail even with permissions: contents: write declared.

Common Errors and Fixes

Error: remote: Permission to user/repo.git denied to github-actions[bot].

Cause: The job lacks contents: write permission.

Fix: Add to the job:

YAML
permissions:
  contents: write

Error: npm ERR! code E403: You do not have permission to publish "package-name".

Cause: The job lacks packages: write, or NODE_AUTH_TOKEN is not set.

Fix:

YAML
permissions:
  packages: write

And ensure your publish step sets:

YAML
env:
  NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Error: Resource not accessible by integration

Cause: The token is trying to access a resource outside its scope — either another repository, an organization-level resource, or a scope not declared in permissions:.

Fix: For same-repository operations, add the required scope. For cross-repository operations, you must use a PAT or GitHub App token instead:

YAML
- uses: actions/checkout@v4
  with:
    token: ${{ secrets.MY_PAT }}

Error: Error: Input required and not supplied: token

Cause: An action requires an explicit token parameter, but none was passed.

Fix: Pass the token explicitly:

YAML
- uses: some/action@v1
  with:
    token: ${{ secrets.GITHUB_TOKEN }}

Production Notes and Security Checks

Limitations of GITHUB_TOKEN

  • Repository-scoped only: Cannot access other repositories in the same organization.
  • Cannot read secrets: Use ${{ secrets.MY_SECRET }} to inject secrets explicitly.
  • Auto-expires: The token is valid only for the duration of the workflow run.
  • No audit trail: The token acts as github-actions[bot], making it harder to trace which workflow performed an action.

Security Recommendations

  1. Apply least privilege: Grant only the scopes each job needs. Avoid blanket permissions: write-all.
  2. Use job-level permissions: Override workflow defaults per job to minimize exposure.
  3. Never log the token: Avoid echo ${{ secrets.GITHUB_TOKEN }} in your workflow.
  4. Prefer GitHub App tokens for cross-repo work: GitHub App Installation Access Tokens (IATs) provide finer-grained control and automatic rotation compared to PATs.
  5. Review workflow permissions regularly: Audit your workflows periodically to ensure permissions match current needs.

When to Use PAT or GitHub App Instead

NeedRecommended Token
Single repository operationsGITHUB_TOKEN
Cross-repository operationsPAT or GitHub App IAT
Organization-level resourcesGitHub App IAT
Long-lived automation outside ActionsPAT (with careful management)

FAQ

Q: Why is my GITHUB_TOKEN read-only by default?

A: GitHub changed the default to read-only in February 2023 as a security hardening measure. This prevents compromised workflows from performing destructive write operations. You must explicitly declare write permissions using the permissions: key.

Q: What's the difference between GITHUB_TOKEN and a PAT?

A: GITHUB_TOKEN is automatically generated per workflow run, scoped to the current repository, and expires when the run ends. A PAT is a user-created, long-lived token that can access multiple repositories and organizations but requires manual management and poses a higher leak risk. For cross-repository or organization-level operations, prefer a GitHub App Installation Access Token.

Q: How do I debug GITHUB_TOKEN permission issues?

A: First, check the job's permissions: block in your workflow file. Then examine the Actions run log — the error message usually identifies the missing scope. You can also verify repository-level permission settings via the GitHub API. If the error persists, test the same operation locally with a token that has known permissions to isolate whether the issue is token-scope-related.

Official References

Related Guides