Summary
The Claude Code Review workflow fails before Claude ever runs
when the pull request was opened by the Copilot coding agent.
The Claude action obtains an OIDC token successfully,
then fails to exchange it for a GitHub App token:
Requesting OIDC token...
OIDC token successfully obtained
Exchanging OIDC token for app token...
App token exchange failed: 401 Unauthorized - User does not have write access on this repository
The exchange is retried twice more and the job fails after about 25 seconds.
Evidence
Failing run:
https://github.com/UCD-SERG/ucd-serg.github.io/actions/runs/30529959398
(claude-review job, 2026-07-30T09:15:33Z -> 09:15:58Z, conclusion failure).
Run metadata:
| field |
value |
event |
pull_request |
triggering_actor |
Copilot |
head_branch |
copilot/fix-claude-review-job |
head_repository |
UCD-SERG/ucd-serg.github.io |
Diagnosis
The App token exchange gates on whether the triggering actor
has write access to the repository.
The triggering actor here is the Copilot coding agent,
which is not in the repository's collaborator list,
so the exchange returns 401 and the job dies before any model call.
For contrast, run
30509709695
on the same repo logged Actor has write access: write and got past this step,
failing later for an unrelated reason
(Command failed: git fetch origin --depth=20 pull/77/head:main).
So the 401 tracks the actor, not the repository configuration.
Note on PR #83
#83
is already open against this,
titled "Fix claude-review workflow: switch to pull_request_target for fork PR support".
Its stated premise looks wrong:
the failing run's head_repository is UCD-SERG/ucd-serg.github.io itself,
not a fork,
so this is not a fork-PR problem.
That matters because pull_request_target is not a neutral swap.
It runs in a write-privileged context against the head branch's code,
which is the standard privilege-escalation footgun for that trigger,
and it would not by itself change whether the Copilot actor has write access.
Worth confirming the diagnosis before merging that approach.
Possible resolutions
Not yet decided, listing options rather than recommending one:
- Skip the review job when the triggering actor is a known bot
that cannot hold write access.
- Grant the Copilot agent write access,
if that is acceptable for this repository.
- Have the review path use a token that does not depend on the triggering
actor's permissions.
Scope
Filed as a tracking issue only, per our report-mistakes-proactively rule.
Found while auditing which repositories hold a CLAUDE_CODE_OAUTH_TOKEN secret;
this is unrelated to the Claude credential itself,
which never got used on this run.
Summary
The
Claude Code Reviewworkflow fails before Claude ever runswhen the pull request was opened by the Copilot coding agent.
The Claude action obtains an OIDC token successfully,
then fails to exchange it for a GitHub App token:
The exchange is retried twice more and the job fails after about 25 seconds.
Evidence
Failing run:
https://github.com/UCD-SERG/ucd-serg.github.io/actions/runs/30529959398
(
claude-reviewjob, 2026-07-30T09:15:33Z -> 09:15:58Z, conclusionfailure).Run metadata:
eventpull_requesttriggering_actorCopilothead_branchcopilot/fix-claude-review-jobhead_repositoryUCD-SERG/ucd-serg.github.ioDiagnosis
The App token exchange gates on whether the triggering actor
has write access to the repository.
The triggering actor here is the
Copilotcoding agent,which is not in the repository's collaborator list,
so the exchange returns 401 and the job dies before any model call.
For contrast, run
30509709695
on the same repo logged
Actor has write access: writeand got past this step,failing later for an unrelated reason
(
Command failed: git fetch origin --depth=20 pull/77/head:main).So the 401 tracks the actor, not the repository configuration.
Note on PR #83
#83
is already open against this,
titled "Fix claude-review workflow: switch to
pull_request_targetfor fork PR support".Its stated premise looks wrong:
the failing run's
head_repositoryisUCD-SERG/ucd-serg.github.ioitself,not a fork,
so this is not a fork-PR problem.
That matters because
pull_request_targetis not a neutral swap.It runs in a write-privileged context against the head branch's code,
which is the standard privilege-escalation footgun for that trigger,
and it would not by itself change whether the
Copilotactor has write access.Worth confirming the diagnosis before merging that approach.
Possible resolutions
Not yet decided, listing options rather than recommending one:
that cannot hold write access.
if that is acceptable for this repository.
actor's permissions.
Scope
Filed as a tracking issue only, per our report-mistakes-proactively rule.
Found while auditing which repositories hold a
CLAUDE_CODE_OAUTH_TOKENsecret;this is unrelated to the Claude credential itself,
which never got used on this run.