GitHub Actions Fork PR Workflow Cannot Comment or Push (Read-Only Token)
A workflow on a pull request from a fork cannot write (comment, label, push, deploy) because GitHub gives fork-PR runs a read-only token by design, no matter what the permissions: block says.
What this error means
On a fork-originated PR, write API calls fail with 403 and secrets are withheld, while the same workflow works for branch PRs in the same repo. The fork PR ran with a read-only token.
RequestError [HttpError]: Resource not accessible by integration (status 403)
# pull_request from a fork ⇒ read-only GITHUB_TOKEN, no secretsCommon causes
Fork-PR token is read-only by design
For pull_request runs from forks, GitHub restricts the token to read-only and withholds secrets, so untrusted contributor code cannot use write access.
Write action attempted in the fork context
Commenting, labeling, pushing, or deploying from the pull_request run of a fork is denied because the token cannot perform those writes.
How to fix it
Use pull_request_target for trusted writes
Handle write-needing steps in a pull_request_target workflow that runs in the base repo context - but check out the base, not untrusted PR code.
on: pull_request_target
permissions:
pull-requests: write
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
script: |
await github.rest.issues.addLabels({ ...context.repo, issue_number: context.issue.number, labels: ['needs-review'] });Keep untrusted code away from secrets
- Never run fork PR code with elevated permissions or secrets.
- Split: run untrusted build/test on pull_request (read-only); do privileged actions on pull_request_target without checking out PR code.
- Use the workflow_run pattern to post results back with elevated permissions safely.
How to prevent it
- Expect read-only tokens and no secrets on fork PRs.
- Use pull_request_target/workflow_run for privileged steps, never on untrusted code.
- Keep secrets out of any run that executes fork-contributed code.