actions/checkout persists the GITHUB_TOKEN for later git commands. If that token is read-only, a push fails with 403. Retrying will not change the token scope.
What this error means
A push after checkout fails with 403 even though credentials are persisted, because the persisted token lacks contents write.
github-actions
remote: Permission to octo/repo.git denied to github-actions[bot].
fatal: unable to access 'https://github.com/octo/repo/': The requested URL returned error: 403
Diagnose it: what token do you actually have?
Permission failures in Actions are almost never about your repository settings alone. Three things combine: the default GITHUB_TOKEN permission set for the repo or organization, the permissions: block in the workflow, and whether the event is a fork pull request, which downgrades the token to read-only regardless of everything else.
Declaring a permissions: block switches the job from the repository default to exactly what you list, so an incomplete block is a common cause of a new failure right after someone tightened security. List every scope the job needs, not just the one that failed.
.github/workflows/ci.yml
permissions:contents:read # checkoutpackages:write # push to GHCRid-token:write # OIDC to a cloud providerpull-requests:write # comment on or label a PRchecks:write # publish check runs
How to prevent it
Set contents: write only on jobs that push.
Do not rely on the persisted token for pushes from fork PRs.
Frequently asked questions
What causes GitHub Actions checkout persisted credentials push 403?
There are 2 common causes: persisted token is read-only and fork run token. checkout persists whatever token it used; if permissions are read-only, the push is denied.
How do I fix GitHub Actions checkout persisted credentials push 403?
Grant write or persist a stronger token. Add permissions: contents: write so the persisted token can push.
What does GitHub Actions checkout persisted credentials push 403 actually mean?
A push after checkout fails with 403 even though credentials are persisted, because the persisted token lacks contents write.
How do I stop GitHub Actions checkout persisted credentials push 403 happening again?
Set contents: write only on jobs that push. The prevention section lists 2 changes that keep it from recurring.