github/github-mcp-server · error
duplicate_of must be provided when state_reason is 'duplicat
Error message
duplicate_of must be provided when state_reason is 'duplicate'
What it means
validateDuplicateState rejects an update_issue call that closes an issue with state_reason="duplicate" but omits the duplicate_of parameter. GitHub requires a duplicate to reference the canonical issue, so the server enforces this before calling the API.
Source
Thrown at pkg/github/issues.go:2731
}
// Return minimal response with just essential information
minimalResponse := MinimalResponse{
ID: fmt.Sprintf("%d", updatedIssue.GetID()),
URL: updatedIssue.GetHTMLURL(),
}
r, err := json.Marshal(minimalResponse)
if err != nil {
return nil, fmt.Errorf("failed to marshal response: %w", err)
}
return utils.NewToolResultText(string(r)), nil
}
func validateDuplicateState(state, stateReason string, duplicateOf int) error {
if state == "closed" && stateReason == "duplicate" && duplicateOf == 0 {
return fmt.Errorf("duplicate_of must be provided when state_reason is 'duplicate'")
}
return nil
}
type updateIssueRequestWithNullableType struct {
github.UpdateIssueRequest
Type *string `json:"type"`
}
func patchIssue(ctx context.Context, client *github.Client, owner, repo string, issueNumber int, issueRequest github.UpdateIssueRequest, issueType string, issueTypeProvided bool) (*github.Issue, *github.Response, error) {
if !issueTypeProvided || issueType != "" {
return client.Issues.Update(ctx, owner, repo, issueNumber, issueRequest)
}
apiURL := fmt.Sprintf("repos/%s/%s/issues/%d", owner, repo, issueNumber)
body := &updateIssueRequestWithNullableType{UpdateIssueRequest: issueRequest}
req, err := client.NewRequest(ctx, http.MethodPatch, apiURL, body)
if err != nil {View on GitHub (pinned to 0ea1f775a7)
Solutions
- Add duplicate_of with the issue number of the canonical (original) issue to the update_issue arguments
- If you did not mean to mark it a duplicate, use state_reason "not_planned" or omit state_reason entirely
- Ensure duplicate_of is passed as a JSON number, not a string
Example fix
// before
{"owner":"octo","repo":"kit","issue_number":42,"state":"closed","state_reason":"duplicate"}
// after
{"owner":"octo","repo":"kit","issue_number":42,"state":"closed","state_reason":"duplicate","duplicate_of":7} Defensive patterns
Strategy: validation
Validate before calling
func validateDuplicateClose(args map[string]any) error {
state, _ := args["state"].(string)
reason, _ := args["state_reason"].(string)
if state == "closed" && reason == "duplicate" {
dup, ok := args["duplicate_of"]
if !ok {
return fmt.Errorf("duplicate_of is required when closing as duplicate")
}
if n, ok := dup.(float64); !ok || n <= 0 {
return fmt.Errorf("duplicate_of must be a positive issue number")
}
}
return nil
} Type guard
func hasValidDuplicateOf(args map[string]any) bool {
if args["state"] != "closed" || args["state_reason"] != "duplicate" {
return true
}
n, ok := args["duplicate_of"].(float64)
return ok && n > 0 && n == float64(int(n))
} Prevention
- Always pair state_reason:'duplicate' with duplicate_of in update_issue templates
- Send duplicate_of as a JSON number referencing the canonical issue
When it happens
Trigger: Calling update_issue with {"state":"closed","state_reason":"duplicate"} and no duplicate_of (or duplicate_of: 0).
Common situations: An LLM or script copies the close-as-duplicate flow but forgets the canonical issue number; passing duplicate_of as a string instead of a number so it decodes to zero.
Related errors
- invalid issue URL %q: %w
- each field_filters entry must be an object
- field_filters must be an array
- field_filters entry: %s
- field_filters entry %q: %s
AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15).
Data as JSON: /api/errors/45eed58ca4aa2f4e.
Report an issue: GitHub.