Repository: cli/cli (Go)

Issue:
Regression: `gh pr create` fails on self-hosted runner in v2.76.0 – “Resource not accessible by integration (organization.teams)”
Note: English isn’t my strongest skill, so I used AI to help translate this issue. If anything looks odd, please feel free to let me know!

### Describe the bug

Running `gh pr create` inside a GitHub Actions workflow on a **self-hosted runner** (GitHub Enterprise Server) fails in `gh` **v2.76.0**, while the same workflow succeeds in **v2.75.1**.

Error message:

```
error fetching organization teams: GraphQL: Resource not accessible by integration (organization.teams)
```

### Affected version

gh version 2.76.0
(v2.75.1 works as expected)

GHE 3.14.7

### Steps to reproduce the behavior

1. On a GitHub Enterprise self-hosted runner, run the following command inside a workflow or shell (authenticated with the default `GITHUB_TOKEN `):

  ```yml
  env:
    GH_ENTERPRISE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    GH_HOST: ${{ secrets.GH_HOST }}
  ```

   ```bash
   gh pr create \
     --title "[Auto Generated] Merge chore/investigate-gh-cli-actions into release20250722" \
     --body "- This PR was automatically created by GitHub Action" \
     --base release20250722 \
     --head chore/investigate-gh-cli-actions \
     --reviewer ${{ github.event.pull_request.user.login }}
   ```

2. Observe the CLI output.
3. The command exits with the GraphQL error shown above.


### Expected vs actual behavior

Expected – The pull request is created successfully without errors.

Actual – The command aborts with
error fetching organization teams: GraphQL: Resource not accessible by integration (organization.teams).

### Logs

```
[git remote -v]
[git config --get-regexp ^remote\..*\.gh-resolved$]
* Request at 2025-07-22 10:57:15.645353278 +0000 UTC m=+0.106273358
* Request to https://***/api/graphql
* Request took 196.853936ms
[git config --get-regexp ^branch\.chore/investigate-gh-cli-actions\.(remote|merge|pushremote|gh-merge-base)$]
* Request at 2025-07-22 10:57:15.846370735 +0000 UTC m=+0.307290806
* Request to https://***/api/graphql
* Request took 132.334393ms
* Request at 2025-07-22 10:57:15.980077053 +0000 UTC m=+0.440997115
* Request to https://***/api/graphql
* Request at 2025-07-22 10:57:15.980144083 +0000 UTC m=+0.441064143
* Request to https://***/api/graphql
* Request at 2025-07-22 10:57:15.980191129 +0000 UTC m=+0.441111171
* Request to https://***/api/graphql
* Request took 61.681612ms
* Request took 75.983812ms
* Request took 294.085325ms
error fetching organization teams: GraphQL: Resource not accessible by integration (organization.teams)
```



Discussion:
👋 Hey @Pittan, thank you for opening this issue ✨ 

I believe this was introduced in https://github.com/cli/cli/pull/11279, and I'm investigating and discussing with the team.

<details><summary>Expand for investigation notes</summary>
<p>

Previously we checked if any user-provided reviewer contained a `/` :

```Go
	var teams []string
	for _, r := range input.Reviewers {
		if i := strings.IndexRune(r, '/'); i > -1 {
			teams = append(teams, r[i+1:])
		} else if !hasUser(r) {
			users = append(users, r)
		}
	}
```

This informed the query builder later to decide whether it would request teams:

```Go
    if len(teams) > 0 {
		fmt.Fprintf(query, "organization(login:%q){\n", repo.RepoOwner())
		for i, t := range teams {
			fmt.Fprintf(query, "t%03d: team(slug:%q){id,slug}\n", i, t)
		}
		fmt.Fprint(query, "}\n")
	}
```

So the conclusion being previously we only requested teams when we believed the user input to contain them.

Now, we always fetch the teams if we have any reviewers:

```Go
	if input.Reviewers {
		g.Go(func() error {
			teams, err := OrganizationTeams(client, repo)
			// TODO: better detection of non-org repos
			if err != nil && !strings.Contains(err.Error(), errorResolvingOrganization) {
				err = fmt.Errorf("error fetching organization teams: %w", err)
				return err
			}
			result.Teams = teams
			return nil
		})
	}
```

</p>
</details> 
@Pittan : a fix for this regression has been released in [`v2.76.1`](https://github.com/cli/cli/releases/tag/v2.76.1) 🙇 
![Image](https://github.com/user-attachments/assets/4e97861f-577c-4ad8-aeec-74cfccd80122)
@BagToad @andyfeller 
Thank you for your quick action 😄 
> Note: English isn’t my strongest skill, so I used AI to help translate this issue. If anything looks odd, please feel free to let me know!
> 
> ### Describe the bug
> Running `gh pr create` inside a GitHub Actions workflow on a **self-hosted runner** (GitHub Enterprise Server) fails in `gh` **v2.76.0**, while the same workflow succeeds in **v2.75.1**.
> 
> Error message:
> 
> ```
> error fetching organization teams: GraphQL: Resource not accessible by integration (organization.teams)
> ```
> 
> ### Affected version
> gh version 2.76.0 (v2.75.1 works as expected)
> 
> GHE 3.14.7
> 
> ### Steps to reproduce the behavior
> 1. On a GitHub Enterprise self-hosted runner, run the following command inside a workflow or shell (authenticated with the default `GITHUB_TOKEN `):
> 
> env:
>   GH_ENTERPRISE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
>   GH_HOST: ${{ secrets.GH_HOST }}
> gh pr create \
>   --title "[Auto Generated] Merge chore/investigate-gh-cli-actions into release20250722" \
>   --body "- This PR was automatically created by GitHub Action" \
>   --base release20250722 \
>   --head chore/investigate-gh-cli-actions \
>   --reviewer ${{ github.event.pull_request.user.login }}
> 2. Observe the CLI output.
> 3. The command exits with the GraphQL error shown above.
> 
> ### Expected vs actual behavior
> Expected – The pull request is created successfully without errors.
> 
> Actual – The command aborts with error fetching organization teams: GraphQL: Resource not accessible by integration (organization.teams).
> 
> ### Logs
> ```
> [git remote -v]
> [git config --get-regexp ^remote\..*\.gh-resolved$]
> * Request at 2025-07-22 10:57:15.645353278 +0000 UTC m=+0.106273358
> * Request to https://***/api/graphql
> * Request took 196.853936ms
> [git config --get-regexp ^branch\.chore/investigate-gh-cli-actions\.(remote|merge|pushremote|gh-merge-base)$]
> * Request at 2025-07-22 10:57:15.846370735 +0000 UTC m=+0.307290806
> * Request to https://***/api/graphql
> * Request took 132.334393ms
> * Request at 2025-07-22 10:57:15.980077053 +0000 UTC m=+0.440997115
> * Request to https://***/api/graphql
> * Request at 2025-07-22 10:57:15.980144083 +0000 UTC m=+0.441064143
> * Request to https://***/api/graphql
> * Request at 2025-07-22 10:57:15.980191129 +0000 UTC m=+0.441111171
> * Request to https://***/api/graphql
> * Request took 61.681612ms
> * Request took 75.983812ms
> * Request took 294.085325ms
> error fetching organization teams: GraphQL: Resource not accessible by integration (organization.teams)
> ```

https://github.com/cli/cli/issues/11360#issue-3252522226
@BagToad @andyfeller when will this CLI update be pushed out to GitHub hosted runners? As of Jul 29, 2025 I'm still seeing version `2.76.0` of the CLI in workflows.

<img width="550" height="292" alt="Image" src="https://github.com/user-attachments/assets/40a51dec-d4f9-41ef-99de-add291267c96" />
oh I see there's an open issue for this: https://github.com/actions/runner-images/issues/12648 
https://github.com/cli/cli/issues/11360#issue-3252522226



Patch:
diff --git a/api/queries_repo.go b/api/queries_repo.go
index 6552aa230cb..324200afd87 100644
--- a/api/queries_repo.go
+++ b/api/queries_repo.go
@@ -919,6 +919,7 @@ type RepoMetadataInput struct {
 	Assignees      bool
 	ActorAssignees bool
 	Reviewers      bool
+	TeamReviewers  bool
 	Labels         bool
 	ProjectsV1     bool
 	ProjectsV2     bool
@@ -964,7 +965,7 @@ func RepoMetadata(client *Client, repo ghrepo.Interface, input RepoMetadataInput
 		}
 	}
 
-	if input.Reviewers {
+	if input.Reviewers && input.TeamReviewers {
 		g.Go(func() error {
 			teams, err := OrganizationTeams(client, repo)
 			// TODO: better detection of non-org repos
diff --git a/pkg/cmd/pr/shared/editable.go b/pkg/cmd/pr/shared/editable.go
index ffe1642da40..9adbeb47c81 100644
--- a/pkg/cmd/pr/shared/editable.go
+++ b/pkg/cmd/pr/shared/editable.go
@@ -429,7 +429,16 @@ func FieldsToEditSurvey(p EditPrompter, editable *Editable) error {
 
 func FetchOptions(client *api.Client, repo ghrepo.Interface, editable *Editable) error {
 	input := api.RepoMetadataInput{
-		Reviewers:      editable.Reviewers.Edited,
+		Reviewers: editable.Reviewers.Edited,
+		// TeamReviewers is always true if Reviewers is true because
+		// this is the existing `pr edit` behavior. This means
+		// always fetch teams.
+		// TODO: evaluate whether this can follow the same logic as
+		// `pr create` to conditionally fetch teams if a reviewer contains
+		// a slash.
+		// See https://github.com/cli/cli/blob/449920b40fc8a5015d1578ca10a301aa385a1914/pkg/cmd/pr/shared/params.go#L67-L71
+		// See https://github.com/cli/cli/issues/11360
+		TeamReviewers:  editable.Reviewers.Edited,
 		Assignees:      editable.Assignees.Edited,
 		ActorAssignees: editable.Assignees.ActorAssignees,
 		Labels:         editable.Labels.Edited,
diff --git a/pkg/cmd/pr/shared/params.go b/pkg/cmd/pr/shared/params.go
index d94f338c5e6..c3315aeaed7 100644
--- a/pkg/cmd/pr/shared/params.go
+++ b/pkg/cmd/pr/shared/params.go
@@ -3,6 +3,7 @@ package shared
 import (
 	"fmt"
 	"net/url"
+	"slices"
 	"strings"
 
 	"github.com/cli/cli/v2/api"
@@ -63,7 +64,10 @@ func AddMetadataToIssueParams(client *api.Client, baseRepo ghrepo.Interface, par
 	// Retrieve minimal information needed to resolve metadata if this was not previously cached from additional metadata survey.
 	if tb.MetadataResult == nil {
 		input := api.RepoMetadataInput{
-			Reviewers:      len(tb.Reviewers) > 0,
+			Reviewers: len(tb.Reviewers) > 0,
+			TeamReviewers: len(tb.Reviewers) > 0 && slices.ContainsFunc(tb.Reviewers, func(r string) bool {
+				return strings.ContainsRune(r, '/')
+			}),
 			Assignees:      len(tb.Assignees) > 0,
 			ActorAssignees: tb.ActorAssignees,
 			Labels:         len(tb.Labels) > 0,


Test patch:
diff --git a/api/queries_repo_test.go b/api/queries_repo_test.go
index c885b99682a..ed6c7a7fdec 100644
--- a/api/queries_repo_test.go
+++ b/api/queries_repo_test.go
@@ -42,12 +42,13 @@ func Test_RepoMetadata(t *testing.T) {
 
 	repo, _ := ghrepo.FromFullName("OWNER/REPO")
 	input := RepoMetadataInput{
-		Assignees:  true,
-		Reviewers:  true,
-		Labels:     true,
-		ProjectsV1: true,
-		ProjectsV2: true,
-		Milestones: true,
+		Assignees:     true,
+		Reviewers:     true,
+		TeamReviewers: true,
+		Labels:        true,
+		ProjectsV1:    true,
+		ProjectsV2:    true,
+		Milestones:    true,
 	}
 
 	http.Register(
@@ -213,6 +214,43 @@ func Test_RepoMetadata(t *testing.T) {
 	}
 }
 
+// Test that RepoMetadata only fetches teams if the input specifies it
+func Test_RepoMetadata_TeamsAreConditionallyFetched(t *testing.T) {
+	http := &httpmock.Registry{}
+	client := newTestClient(http)
+	repo, _ := ghrepo.FromFullName("OWNER/REPO")
+	input := RepoMetadataInput{
+		Reviewers:     true,
+		TeamReviewers: false, // Do not fetch teams
+	}
+
+	http.Register(
+		httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+		httpmock.StringResponse(`
+		{ "data": { "repository": { "assignableUsers": {
+			"nodes": [
+				{ "login": "hubot", "id": "HUBOTID" },
+				{ "login": "MonaLisa", "id": "MONAID" }
+			],
+			"pageInfo": { "hasNextPage": false }
+		} } } }
+		`))
+
+	http.Register(
+		httpmock.GraphQL(`query UserCurrent\b`),
+		httpmock.StringResponse(`
+		  { "data": { "viewer": { "login": "monalisa" } } }
+		`))
+
+	http.Exclude(
+		t,
+		httpmock.GraphQL(`query OrganizationTeamList\b`),
+	)
+
+	_, err := RepoMetadata(client, repo, input)
+	require.NoError(t, err)
+}
+
 func Test_ProjectNamesToPaths(t *testing.T) {
 	t.Run("when projectsV1 is supported, requests them", func(t *testing.T) {
 		http := &httpmock.Registry{}
diff --git a/pkg/cmd/pr/create/create_test.go b/pkg/cmd/pr/create/create_test.go
index 41fd10ab023..46942867030 100644
--- a/pkg/cmd/pr/create/create_test.go
+++ b/pkg/cmd/pr/create/create_test.go
@@ -1314,14 +1314,6 @@ func Test_createRun(t *testing.T) {
 							"pageInfo": { "hasNextPage": false }
 						} } } }
 					`))
-				reg.Register(
-					httpmock.GraphQL(`query OrganizationTeamList\b`),
-					httpmock.StringResponse(`
-					{ "data": { "organization": { "teams": {
-						"nodes": [],
-						"pageInfo": { "hasNextPage": false }
-					} } } }
-					`))
 				reg.Register(
 					httpmock.GraphQL(`mutation PullRequestCreateRequestReviews\b`),
 					httpmock.GraphQLMutation(`
@@ -1662,6 +1654,125 @@ func Test_createRun(t *testing.T) {
 			},
 			expectedOut: "https://github.com/OWNER/REPO/pull/12\n",
 		},
+		{
+			name: "if reviewer contains any team, fetch teams",
+			setup: func(opts *CreateOptions, t *testing.T) func() {
+				opts.TitleProvided = true
+				opts.BodyProvided = true
+				opts.Title = "my title"
+				opts.Body = "my body"
+				opts.Reviewers = []string{"hubot", "monalisa", "org/core", "org/robots"}
+				opts.HeadBranch = "feature"
+				return func() {}
+			},
+			httpStubs: func(reg *httpmock.Registry, t *testing.T) {
+				reg.Register(
+					httpmock.GraphQL(`mutation PullRequestCreate\b`),
+					httpmock.GraphQLMutation(`
+						{ "data": { "createPullRequest": { "pullRequest": {
+							"URL": "https://github.com/OWNER/REPO/pull/12",
+							"id": "NEWPULLID"
+						} } } }`,
+						func(input map[string]interface{}) {}))
+				reg.Register(
+					httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+					httpmock.StringResponse(`
+						{ "data": { "repository": { "assignableUsers": {
+							"nodes": [
+								{ "login": "hubot", "id": "HUBOTID" },
+								{ "login": "MonaLisa", "id": "MONAID" }
+							],
+							"pageInfo": { "hasNextPage": false }
+						} } } }
+					`))
+				reg.Register(
+					httpmock.GraphQL(`query UserCurrent\b`),
+					httpmock.StringResponse(`
+						{ "data": { "viewer": { "login": "monalisa" } } }
+					`))
+				reg.Register(
+					httpmock.GraphQL(`query OrganizationTeamList\b`),
+					httpmock.StringResponse(`
+					{ "data": { "organization": { "teams": {
+						"nodes": [
+							{ "slug": "core", "id": "COREID" },
+							{ "slug": "robots", "id": "ROBOTID" }
+						],
+						"pageInfo": { "hasNextPage": false }
+					} } } }
+					`))
+				reg.Register(
+					httpmock.GraphQL(`mutation PullRequestCreateRequestReviews\b`),
+					httpmock.GraphQLMutation(`
+					{ "data": { "requestReviews": {
+						"clientMutationId": ""
+					} } }
+				`, func(inputs map[string]interface{}) {
+						assert.Equal(t, "NEWPULLID", inputs["pullRequestId"])
+						assert.Equal(t, []interface{}{"HUBOTID", "MONAID"}, inputs["userIds"])
+						assert.Equal(t, []interface{}{"COREID", "ROBOTID"}, inputs["teamIds"])
+						assert.Equal(t, true, inputs["union"])
+					}))
+			},
+			expectedOut:    "https://github.com/OWNER/REPO/pull/12\n",
+			expectedErrOut: "",
+		},
+		{
+			name: "if reviewer does NOT contain any team, do NOT fetch teams",
+			setup: func(opts *CreateOptions, t *testing.T) func() {
+				opts.TitleProvided = true
+				opts.BodyProvided = true
+				opts.Title = "my title"
+				opts.Body = "my body"
+				opts.Reviewers = []string{"hubot", "monalisa"}
+				opts.HeadBranch = "feature"
+				return func() {}
+			},
+			httpStubs: func(reg *httpmock.Registry, t *testing.T) {
+				reg.Register(
+					httpmock.GraphQL(`mutation PullRequestCreate\b`),
+					httpmock.GraphQLMutation(`
+						{ "data": { "createPullRequest": { "pullRequest": {
+							"URL": "https://github.com/OWNER/REPO/pull/12",
+							"id": "NEWPULLID"
+						} } } }`,
+						func(input map[string]interface{}) {}))
+				reg.Register(
+					httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+					httpmock.StringResponse(`
+						{ "data": { "repository": { "assignableUsers": {
+							"nodes": [
+								{ "login": "hubot", "id": "HUBOTID" },
+								{ "login": "MonaLisa", "id": "MONAID" }
+							],
+							"pageInfo": { "hasNextPage": false }
+						} } } }
+					`))
+				reg.Register(
+					httpmock.GraphQL(`query UserCurrent\b`),
+					httpmock.StringResponse(`
+						{ "data": { "viewer": { "login": "monalisa" } } }
+					`))
+				reg.Exclude(
+					t,
+					httpmock.GraphQL(`query OrganizationTeamList\b`),
+				)
+				reg.Register(
+					httpmock.GraphQL(`mutation PullRequestCreateRequestReviews\b`),
+					httpmock.GraphQLMutation(`
+					{ "data": { "requestReviews": {
+						"clientMutationId": ""
+					} } }
+				`, func(inputs map[string]interface{}) {
+						assert.Equal(t, "NEWPULLID", inputs["pullRequestId"])
+						assert.Equal(t, []interface{}{"HUBOTID", "MONAID"}, inputs["userIds"])
+						assert.NotEqual(t, []interface{}{"COREID", "ROBOTID"}, inputs["teamIds"])
+						assert.Equal(t, true, inputs["union"])
+					}))
+			},
+			expectedOut:    "https://github.com/OWNER/REPO/pull/12\n",
+			expectedErrOut: "",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {