github/github-mcp-server · error

failed to list releases: %w

Error message

failed to list releases: %w

What it means

Returned by list_releases when client.Repositories.ListReleases (GET /repos/{owner}/{repo}/releases) itself fails. Unlike every neighboring call in this file, the handler wraps it with a bare fmt.Errorf instead of ghErrors.NewGitHubAPIErrorResponse, so the structured status/message from go-github's *github.ErrorResponse survives only inside the %w chain. Triggers: 404 (repo unknown or invisible to the token), 401 (bad token), 403 (rate limit or forbidden), and pure network failures where the call never completes.

Source

Thrown at pkg/github/repositories.go:1852

			}
			pagination, err := OptionalPaginationParams(args)
			if err != nil {
				return utils.NewToolResultError(err.Error()), nil, nil
			}

			opts := &github.ListOptions{
				Page:    pagination.Page,
				PerPage: pagination.PerPage,
			}

			client, err := deps.GetClient(ctx)
			if err != nil {
				return nil, nil, fmt.Errorf("failed to get GitHub client: %w", err)
			}

			releases, resp, err := client.Repositories.ListReleases(ctx, owner, repo, opts)
			if err != nil {
				return nil, nil, fmt.Errorf("failed to list releases: %w", err)
			}
			defer func() { _ = resp.Body.Close() }()

			if resp.StatusCode != http.StatusOK {
				body, err := io.ReadAll(resp.Body)
				if err != nil {
					return nil, nil, fmt.Errorf("failed to read response body: %w", err)
				}
				return ghErrors.NewGitHubAPIStatusErrorResponse(ctx, "failed to list releases", resp, body), nil, nil
			}

			minimalReleases := make([]MinimalRelease, 0, len(releases))
			for _, release := range releases {
				if release != nil {
					minimalReleases = append(minimalReleases, convertToMinimalRelease(release))
				}
			}

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Reproduce with the same token: `gh api repos/OWNER/REPO/releases --jq length` shows the raw 404/403/401 directly.
  2. 404: fix owner/repo spelling or grant the token access to the repo.
  3. 403 rate limit: check `gh api rate_limit`, wait for reset, retry with backoff.
  4. 401: rotate/refresh the token.
  5. Persistent: read the wrapped *github.ErrorResponse.Message in server logs for the exact cause.

Example fix

// before
releases, resp, err := client.Repositories.ListReleases(ctx, owner, repo, opts)
if err != nil {
	return nil, nil, fmt.Errorf("failed to list releases: %w", err)
}

// after: structured API error like every neighboring handler
releases, resp, err := client.Repositories.ListReleases(ctx, owner, repo, opts)
if err != nil {
	return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to list releases", resp, err), nil, nil
}
Defensive patterns

Strategy: try-catch

Validate before calling

# same token as the MCP server uses
gh api 'repos/OWNER/REPO/releases?per_page=1' --jq 'length'  # 404 -> repo invisible to token
gh api rate_limit --jq '.resources.core.remaining'            # 0 -> expect 403 failures

Try / catch

// when embedding the handler's client
releases, resp, err := client.Repositories.ListReleases(ctx, owner, repo, opts)
if err != nil {
	var ghErr *github.ErrorResponse
	if errors.As(err, &ghErr) {
		switch ghErr.Response.StatusCode {
		case http.StatusNotFound: // unknown repo or token lacks access
		case http.StatusUnauthorized: // expired/revoked token
		case http.StatusForbidden: // rate limit - back off until reset
		}
	}
}

Prevention

When it happens

Trigger: Listing releases on a nonexistent repo (404); a fine-grained PAT without access to the private repo (404/403); primary rate limit exhausted (403 'API rate limit exceeded'); expired token (401); DNS/TLS failure reaching api.github.com.

Common situations: Typo'd owner/repo; pointing the tool at a fork or private repo the token cannot see; CI jobs exhausting token quota mid-run; GHES host unreachable.

Related errors


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