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

  1. Add duplicate_of with the issue number of the canonical (original) issue to the update_issue arguments
  2. If you did not mean to mark it a duplicate, use state_reason "not_planned" or omit state_reason entirely
  3. 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

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


AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15). Data as JSON: /api/errors/45eed58ca4aa2f4e. Report an issue: GitHub.