From 521668931957f96291e8d21234ff12b12108c9be Mon Sep 17 00:00:00 2001 From: Justin Willhite <5132924+thejdubb02@users.noreply.github.com> Date: Thu, 3 Sep 2026 22:55:15 +0000 Subject: [PATCH] Return a clear error for missing owner/repo/issue_number in the copilot assignment tools assign_copilot_to_issue and assign_copilot_to_issue_with_intent decode owner, repo and issue_number with mapstructure.WeakDecode, which zero-fills a missing value instead of erroring. A missing required arg then reached the GraphQL query and surfaced as a confusing "failed to get suggested actors: Could not resolve to a Repository" error. Reject the zero values after decoding, matching the is_suggestion and rationale/confidence checks in the same handlers, so the caller gets "missing required parameter: ". Adds table tests for the missing cases. --- pkg/github/copilot.go | 26 +++++++++++++++++++++++ pkg/github/copilot_test.go | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/pkg/github/copilot.go b/pkg/github/copilot.go index 1064bef144..e5db071499 100644 --- a/pkg/github/copilot.go +++ b/pkg/github/copilot.go @@ -214,6 +214,19 @@ func AssignCopilotToIssue(t translations.TranslationHelperFunc) inventory.Server return utils.NewToolResultError(err.Error()), nil, nil } + // owner, repo and issue_number are required, but WeakDecode zero-fills a + // missing value, so a missing arg reached the query as a confusing + // "Could not resolve to a Repository" error. Reject the zero values. + if params.Owner == "" { + return utils.NewToolResultError("missing required parameter: owner"), nil, nil + } + if params.Repo == "" { + return utils.NewToolResultError("missing required parameter: repo"), nil, nil + } + if params.IssueNumber == 0 { + return utils.NewToolResultError("missing required parameter: issue_number"), nil, nil + } + client, err := deps.GetGQLClient(ctx) if err != nil { return nil, nil, fmt.Errorf("failed to get GitHub client: %w", err) @@ -588,6 +601,19 @@ func AssignCopilotToIssueWithIntent(t translations.TranslationHelperFunc) invent return utils.NewToolResultError(err.Error()), nil, nil } + // owner, repo and issue_number are required, but WeakDecode zero-fills a + // missing value, so reject the zero values (as with rationale/confidence + // below) before they reach the query as a confusing repository error. + if params.Owner == "" { + return utils.NewToolResultError("missing required parameter: owner"), nil, nil + } + if params.Repo == "" { + return utils.NewToolResultError("missing required parameter: repo"), nil, nil + } + if params.IssueNumber == 0 { + return utils.NewToolResultError("missing required parameter: issue_number"), nil, nil + } + // Validate rationale length (rune count, matching the granular assignee tools). rationale := strings.TrimSpace(params.Rationale) if rationale == "" { diff --git a/pkg/github/copilot_test.go b/pkg/github/copilot_test.go index c90de4804b..3abaaf00c6 100644 --- a/pkg/github/copilot_test.go +++ b/pkg/github/copilot_test.go @@ -59,6 +59,36 @@ func TestAssignCopilotToIssue(t *testing.T) { expectToolError bool expectedToolErrMsg string }{ + { + name: "missing owner is rejected", + requestArgs: map[string]any{ + "repo": "repo", + "issue_number": float64(123), + }, + mockedClient: githubv4mock.NewMockedHTTPClient(), + expectToolError: true, + expectedToolErrMsg: "missing required parameter: owner", + }, + { + name: "missing repo is rejected", + requestArgs: map[string]any{ + "owner": "owner", + "issue_number": float64(123), + }, + mockedClient: githubv4mock.NewMockedHTTPClient(), + expectToolError: true, + expectedToolErrMsg: "missing required parameter: repo", + }, + { + name: "missing issue_number is rejected", + requestArgs: map[string]any{ + "owner": "owner", + "repo": "repo", + }, + mockedClient: githubv4mock.NewMockedHTTPClient(), + expectToolError: true, + expectedToolErrMsg: "missing required parameter: issue_number", + }, { name: "successful assignment when there are no existing assignees", requestArgs: map[string]any{ @@ -1228,6 +1258,19 @@ func TestAssignCopilotToIssueWithIntent(t *testing.T) { expectedToolErrMsg string expectSuggestion bool }{ + { + name: "missing owner is rejected", + requestArgs: map[string]any{ + "repo": "repo", + "issue_number": float64(123), + "rationale": "Well-scoped task.", + "confidence": "HIGH", + "is_suggestion": false, + }, + mockedClient: githubv4mock.NewMockedHTTPClient(), + expectToolError: true, + expectedToolErrMsg: "missing required parameter: owner", + }, { name: "direct assignment with rationale and confidence preserves existing assignees", requestArgs: map[string]any{