github/github-mcp-server · error

creating installation token request: %w

Error message

creating installation token request: %w

What it means

http.NewRequestWithContext rejected the endpoint URL built from BaseRESTURL. NewRequestWithContext calls url.Parse on the final joined string, so it fails only when the URL is still unparseable — a base that survived JoinPath but contains characters the stricter request parser rejects, or an empty URL after joining. Like error 102 this is a configuration-shape failure that occurs before any socket is opened.

Source

Thrown at internal/githubapp/githubapp.go:141

}

func (s *installationTokenSource) Token() (*oauth2.Token, error) {
	jwt, err := mintJWT(s.cfg.AppID, s.privateKey, time.Now())
	if err != nil {
		return nil, err
	}

	endpoint, err := url.JoinPath(s.cfg.BaseRESTURL, "app", "installations", s.cfg.InstallationID, "access_tokens")
	if err != nil {
		return nil, fmt.Errorf("building installation token URL: %w", err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), httpTimeout)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, nil)
	if err != nil {
		return nil, fmt.Errorf("creating installation token request: %w", err)
	}
	req.Header.Set("Authorization", "Bearer "+jwt)
	req.Header.Set("Accept", "application/vnd.github+json")
	req.Header.Set("X-GitHub-Api-Version", "2022-11-28")

	resp, err := s.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("requesting installation token: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusCreated {
		snippet, readErr := io.ReadAll(io.LimitReader(resp.Body, 512))
		if readErr != nil {
			return nil, fmt.Errorf("installation token request failed: %s (reading response: %w)", resp.Status, readErr)
		}
		return nil, fmt.Errorf("installation token request failed: %s: %s", resp.Status, strings.TrimSpace(string(snippet)))
	}

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Sanitize BaseRESTURL and InstallationID (strip CR/LF/spaces) before building the Config
  2. Confirm InstallationID is digits-only — GitHub installation IDs are numeric strings
  3. Validate the final URL with url.ParseRequestURI in a startup check to catch this before first token refresh

Example fix

// before
installationID := rawUserInput // " 42
" -> creating installation token request: ...

// after
installationID := strings.TrimSpace(rawUserInput)
if !regexp.MustCompile(`^[0-9]+$`).MatchString(installationID) {
    return errors.New("installation ID must be numeric")
}
Defensive patterns

Strategy: validation

Validate before calling

func validInstallationID(s string) bool { return regexp.MustCompile(`^[0-9]+$`).MatchString(s) }

// and before building Config:
if _, err := url.ParseRequestURI(base); err != nil { return err }

Prevention

When it happens

Trigger: The joined endpoint string (e.g. BaseRESTURL + 'app/installations/{id}/access_tokens') contains a control character (0x7f, raw newline), a space in the host, or InstallationID text that makes the path invalid, causing http.NewRequestWithContext at internal/githubapp/githubapp.go:139 to return a parse error.

Common situations: BaseRESTURL loaded from a .env file with embedded CR ( ) from Windows line endings; an InstallationID taken from user input containing spaces or '%zz' malformed escapes; a config value that is actually empty after JoinPath normalization.

Related errors


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