Description
When a GitHub App (e.g., Copilot) triggers a pull_request_review event, the context.actor is the App's login (e.g., Copilot). This actor is not a GitHub user, so getCollaboratorPermissionLevel() returns a 404.
In check_membership.cjs, this 404 is returned as {authorized: false, error: "Copilot is not a user"} from checkRepositoryPermission(). The calling code then hits the error branch and exits immediately — before ever checking the GH_AW_ALLOWED_BOTS fallback.
Impact
The bots: field in workflow frontmatter is effectively broken for any GitHub App whose context.actor is not a valid GitHub user. The bot is correctly listed in GH_AW_ALLOWED_BOTS in the compiled lock file, but check_membership.cjs never evaluates it.
Example: You want to build an autonomous review pipeline where Copilot reviews a PR, and a gh-aw agent responds to the review (e.g., addresses comments, approves, or merges). You configure:
on:
pull_request_review:
types: [submitted]
bots: [Copilot]
This compiles correctly — GH_AW_ALLOWED_BOTS: Copilot appears in the lock file. But when Copilot submits a review:
pre_activation runs check_membership.cjs
- Role check calls
getCollaboratorPermissionLevel("Copilot") → 404 → {error: "Copilot is not a user"}
- The
if (result.error) branch fires → sets is_team_member=false, result=api_error → returns
- The bot allowlist check (
isAllowedBot, checkBotStatus) is never reached
activated output = false → activation and agent jobs are skipped
- The agent never runs despite the bot being explicitly allowed
The workflow appears to work (no errors, pre_activation shows success) but silently does nothing. There is no log line indicating the bot check was attempted, making this difficult to debug.
Root Cause (in check_membership.cjs)
const result = await checkRepositoryPermission(actor, owner, repo, requiredPermissions);
if (result.error) {
// EXIT HERE — never reaches bot check below
core.setOutput("is_team_member", "false");
core.setOutput("result", "api_error");
return;
}
if (result.authorized) {
// ...
} else {
// Bot fallback lives here — unreachable when result.error is set
if (allowedBots && allowedBots.length > 0) {
if (isAllowedBot(actor, allowedBots)) {
// ...
}
}
}
Repro Steps
- Create a gh-aw workflow triggered by
pull_request_review
- Add a bot to the allowlist under
on::
on:
pull_request_review:
types: [submitted]
bots: [Copilot]
- Compile with
gh aw compile — lock file correctly shows GH_AW_ALLOWED_BOTS: Copilot
- Have Copilot submit a review on a PR
- Observe:
pre_activation job succeeds, but activated output is false → activation and agent jobs are skipped
- In
pre_activation logs:
GH_AW_ALLOWED_BOTS: Copilot
Checking if user 'Copilot' has required permissions
⚠️ Repository permission check failed: Copilot is not a user
No subsequent log line about checking the bots list.
Expected Behavior
When checkRepositoryPermission fails with an error for an actor that IS in GH_AW_ALLOWED_BOTS, the code should fall through to the bot allowlist check instead of exiting early.
Suggested Fix
Move the bot fallback check so it runs when result.error is set AND the actor is in the allowed bots list:
const result = await checkRepositoryPermission(actor, owner, repo, requiredPermissions);
if (result.authorized) {
// ... authorized by role
} else {
// Check bot fallback (whether error or insufficient permissions)
if (allowedBots && allowedBots.length > 0 && isAllowedBot(actor, allowedBots)) {
const botStatus = await checkBotStatus(actor, owner, repo);
if (botStatus.isBot && botStatus.isActive) {
// ... authorized as bot
return;
}
}
// Not authorized by role or bot
if (result.error) {
core.setOutput("result", "api_error");
} else {
core.setOutput("result", "insufficient_permissions");
}
core.setOutput("is_team_member", "false");
}
Description
When a GitHub App (e.g., Copilot) triggers a
pull_request_reviewevent, thecontext.actoris the App's login (e.g.,Copilot). This actor is not a GitHub user, sogetCollaboratorPermissionLevel()returns a 404.In
check_membership.cjs, this 404 is returned as{authorized: false, error: "Copilot is not a user"}fromcheckRepositoryPermission(). The calling code then hits theerrorbranch and exits immediately — before ever checking theGH_AW_ALLOWED_BOTSfallback.Impact
The
bots:field in workflow frontmatter is effectively broken for any GitHub App whosecontext.actoris not a valid GitHub user. The bot is correctly listed inGH_AW_ALLOWED_BOTSin the compiled lock file, butcheck_membership.cjsnever evaluates it.Example: You want to build an autonomous review pipeline where Copilot reviews a PR, and a gh-aw agent responds to the review (e.g., addresses comments, approves, or merges). You configure:
This compiles correctly —
GH_AW_ALLOWED_BOTS: Copilotappears in the lock file. But when Copilot submits a review:pre_activationrunscheck_membership.cjsgetCollaboratorPermissionLevel("Copilot")→ 404 →{error: "Copilot is not a user"}if (result.error)branch fires → setsis_team_member=false,result=api_error→ returnsisAllowedBot,checkBotStatus) is never reachedactivatedoutput =false→activationandagentjobs are skippedThe workflow appears to work (no errors,
pre_activationshowssuccess) but silently does nothing. There is no log line indicating the bot check was attempted, making this difficult to debug.Root Cause (in
check_membership.cjs)Repro Steps
pull_request_reviewon::gh aw compile— lock file correctly showsGH_AW_ALLOWED_BOTS: Copilotpre_activationjob succeeds, butactivatedoutput isfalse→activationandagentjobs are skippedpre_activationlogs:Expected Behavior
When
checkRepositoryPermissionfails with an error for an actor that IS inGH_AW_ALLOWED_BOTS, the code should fall through to the bot allowlist check instead of exiting early.Suggested Fix
Move the bot fallback check so it runs when
result.erroris set AND the actor is in the allowed bots list: