From dde46dc42406db437458c5151b428a9318b743b3 Mon Sep 17 00:00:00 2001 From: Max Beizer Date: Fri, 24 Apr 2026 17:16:00 -0500 Subject: [PATCH] Add "Resource not accessible" to ProjectsV2IgnorableError When a token (GitHub App, fine-grained PAT, or GITHUB_TOKEN) lacks the project permission, querying projectItems on a PR or issue fails with "Resource not accessible by integration" or "Resource not accessible by personal access token". ProjectsV2IgnorableError did not match these errors, causing commands like pr view, pr edit, and issue view to fail entirely instead of gracefully omitting project data. Add "Resource not accessible by" as an ignorable error prefix. This is safe because ProjectsV2IgnorableError is only called in project-specific code paths. Closes #13280 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- api/queries_projects_v2.go | 25 ++++++++++++++----------- api/queries_projects_v2_test.go | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/api/queries_projects_v2.go b/api/queries_projects_v2.go index 0126c1caa3b..b5f46655d7b 100644 --- a/api/queries_projects_v2.go +++ b/api/queries_projects_v2.go @@ -9,12 +9,13 @@ import ( ) const ( - errorProjectsV2ReadScope = "field requires one of the following scopes: ['read:project']" - errorProjectsV2UserField = "Field 'projectsV2' doesn't exist on type 'User'" - errorProjectsV2RepositoryField = "Field 'projectsV2' doesn't exist on type 'Repository'" - errorProjectsV2OrganizationField = "Field 'projectsV2' doesn't exist on type 'Organization'" - errorProjectsV2IssueField = "Field 'projectItems' doesn't exist on type 'Issue'" - errorProjectsV2PullRequestField = "Field 'projectItems' doesn't exist on type 'PullRequest'" + errorProjectsV2ReadScope = "field requires one of the following scopes: ['read:project']" + errorProjectsV2UserField = "Field 'projectsV2' doesn't exist on type 'User'" + errorProjectsV2RepositoryField = "Field 'projectsV2' doesn't exist on type 'Repository'" + errorProjectsV2OrganizationField = "Field 'projectsV2' doesn't exist on type 'Organization'" + errorProjectsV2IssueField = "Field 'projectItems' doesn't exist on type 'Issue'" + errorProjectsV2PullRequestField = "Field 'projectItems' doesn't exist on type 'PullRequest'" + errorProjectsV2ResourceNotAccessible = "Resource not accessible by" ) type ProjectV2 struct { @@ -321,10 +322,11 @@ func CurrentUserProjectsV2(client *Client, hostname string) ([]ProjectV2, error) } // When querying ProjectsV2 fields we generally don't want to show the user -// scope errors and field does not exist errors. ProjectsV2IgnorableError -// checks against known error strings to see if an error can be safely ignored. -// Due to the fact that the GraphQLClient can return multiple types of errors -// this uses brittle string comparison to check against the known error strings. +// scope errors, field does not exist errors, or authorization errors. +// ProjectsV2IgnorableError checks against known error strings to see if an +// error can be safely ignored. Due to the fact that the GraphQLClient can +// return multiple types of errors this uses brittle string comparison to check +// against the known error strings. func ProjectsV2IgnorableError(err error) bool { msg := err.Error() if strings.Contains(msg, errorProjectsV2ReadScope) || @@ -332,7 +334,8 @@ func ProjectsV2IgnorableError(err error) bool { strings.Contains(msg, errorProjectsV2RepositoryField) || strings.Contains(msg, errorProjectsV2OrganizationField) || strings.Contains(msg, errorProjectsV2IssueField) || - strings.Contains(msg, errorProjectsV2PullRequestField) { + strings.Contains(msg, errorProjectsV2PullRequestField) || + strings.Contains(msg, errorProjectsV2ResourceNotAccessible) { return true } return false diff --git a/api/queries_projects_v2_test.go b/api/queries_projects_v2_test.go index 3d29a19c144..1f1d91b8295 100644 --- a/api/queries_projects_v2_test.go +++ b/api/queries_projects_v2_test.go @@ -317,6 +317,21 @@ func TestProjectsV2IgnorableError(t *testing.T) { errMsg: "Field 'projectItems' doesn't exist on type 'PullRequest'", expectOut: true, }, + { + name: "resource not accessible by integration", + errMsg: "Resource not accessible by integration", + expectOut: true, + }, + { + name: "resource not accessible by personal access token", + errMsg: "Resource not accessible by personal access token", + expectOut: true, + }, + { + name: "resource not accessible by integration with path context", + errMsg: "GraphQL: Resource not accessible by integration (repository.pullRequest.projectItems.nodes.0)", + expectOut: true, + }, { name: "other error", errMsg: "some other graphql error message",