github/github-mcp-server · error
invalid issue URL %q: %w
Error message
invalid issue URL %q: %w
What it means
issueNumberFromIssueURL extracts everything after the last '/' in a string and runs strconv.Atoi on it. The error fires when that trailing segment is not a bare integer. Callers pass comment.GetIssueURL() — the issue_url field from GitHub's comment API — so failures mean the URL was empty, carried a trailing slash, or had a non-numeric suffix. Notably LastIndex returns -1 on a slash-free string, making Atoi parse the whole input.
Source
Thrown at pkg/github/issues.go:1379
result = reactionResponse
default:
result = commentResponse
}
r, err := json.Marshal(result)
if err != nil {
return utils.NewToolResultErrorFromErr("failed to marshal response", err), nil, nil
}
return utils.NewToolResultText(string(r)), nil, nil
})
}
func issueNumberFromIssueURL(issueURL string) (int, error) {
issueNumberString := issueURL[strings.LastIndex(issueURL, "/")+1:]
issueNumber, err := strconv.Atoi(issueNumberString)
if err != nil {
return 0, fmt.Errorf("invalid issue URL %q: %w", issueURL, err)
}
return issueNumber, nil
}
// SubIssueWrite creates a tool to add a sub-issue to a parent issue.
func SubIssueWrite(t translations.TranslationHelperFunc) inventory.ServerTool {
st := NewTool(
ToolsetMetadataIssues,
mcp.Tool{
Name: "sub_issue_write",
Description: t("TOOL_SUB_ISSUE_WRITE_DESCRIPTION", "Add a sub-issue to a parent issue in a GitHub repository."),
Annotations: &mcp.ToolAnnotations{
Title: t("TOOL_SUB_ISSUE_WRITE_USER_TITLE", "Change sub-issue"),
ReadOnlyHint: false,
},
InputSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{View on GitHub (pinned to 0ea1f775a7)
Solutions
- Trim trailing slashes and query/fragment before extracting the number
- Parse the URL properly with net/url and take the last numeric path segment
- If the input is already a bare number, skip URL parsing entirely
- Validate with a regexp like ^https?://[^/]+/[^/]+/[^/]+/issues/\d+$ before calling
Example fix
// before
issueNumberString := issueURL[strings.LastIndex(issueURL, "/")+1:]
issueNumber, err := strconv.Atoi(issueNumberString)
// after
u, err := url.Parse(issueURL)
if err != nil {
return 0, fmt.Errorf("invalid issue URL %q: %w", issueURL, err)
}
seg := strings.Trim(u.Path, "/")
issueNumber, err := strconv.Atoi(seg[strings.LastIndex(seg, "/")+1:])
if err != nil {
return 0, fmt.Errorf("invalid issue URL %q: %w", issueURL, err)
} Defensive patterns
Strategy: validation
Validate before calling
// Validate before calling anything that derives a number from a URL
var issueURLRe = regexp.MustCompile(`^https?://[^/]+/[^/]+/[^/]+/issues/(\d+|)$`)
func isValidIssueURL(u string) bool {
u = strings.TrimRight(u, "/?#")
return issueURLRe.MatchString(strings.TrimSuffix(u, "/") + "/") || regexp.MustCompile(`^\d+$`).MatchString(u)
} Type guard
var issueNumRe = regexp.MustCompile(`/(?:issues)/(\d+)(?:[/?#].*)?$`)
func issueNumberFromURL(u string) (int, bool) {
m := issueNumRe.FindStringSubmatch(u)
if m == nil { return 0, false }
n, err := strconv.Atoi(m[1])
return n, err == nil
} Try / catch
num, err := issueNumberFromIssueURL(rawURL)
if err != nil {
if n, ok := issueNumberFromURL(strings.TrimRight(rawURL, "/")); ok {
num = n // recover from trailing slash / fragments
} else {
return fmt.Errorf("need an issue URL like .../issues/123: %w", err)
}
} Prevention
- Trim trailing '/', '?' and '#' from user- or API-supplied URLs before extracting segments
- Prefer net/url parsing over LastIndex string surgery
- Reject empty strings early — they parse as the whole string and always fail
- Where the API offers a numeric field (e.g. comment.issue_number via GraphQL), take the number instead of parsing URLs
When it happens
Trigger: issue_url is empty (""); URL has a trailing slash so the last segment is ""; URL ends with a query string or fragment instead of the number; a caller passes a number-less string like "https://github.com/o/r/issues/".
Common situations: API responses where issue_url is unexpectedly absent for deleted/anonymized issues; callers passing hand-built or copy-pasted URLs with trailing slashes; schema changes in comment payloads after GitHub API version bumps.
Related errors
- failed to get issue comments: %w
- duplicate_of must be provided when state_reason is 'duplicat
- each field_filters entry must be an object
- field_filters must be an array
- field_filters entry: %s
AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15).
Data as JSON: /api/errors/534026f8e77151a0.
Report an issue: GitHub.