github/github-mcp-server · error

building installation token URL: %w

Error message

building installation token URL: %w

What it means

url.JoinPath returned an error while composing {BaseRESTURL}/app/installations/{id}/access_tokens. JoinPath parses BaseRESTURL first, so this fires when the configured base is not a valid absolute URL — typically a missing scheme or illegal control characters. It is a configuration error in Config.BaseRESTURL, not a network problem.

Source

Thrown at internal/githubapp/githubapp.go:133

	httpClient *http.Client
}

func newInstallationTokenSource(cfg Config, privateKey *rsa.PrivateKey, httpClient *http.Client) *installationTokenSource {
	if httpClient == nil {
		httpClient = &http.Client{Timeout: httpTimeout}
	}
	return &installationTokenSource{cfg: cfg, privateKey: privateKey, httpClient: httpClient}
}

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() }()

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Set BaseRESTURL with an explicit scheme: https://api.github.com/ for github.com, https://HOST/api/v3/ for GitHub Enterprise Server
  2. Trim whitespace/newlines when loading the value from env or config files
  3. Validate the URL at startup with url.Parse and check .IsAbs() before constructing the provider

Example fix

// before
cfg := githubapp.Config{BaseRESTURL: os.Getenv("GH_API_URL"), ...} // "ghe.example.com/api/v3"

// after
base := strings.TrimSpace(os.Getenv("GH_API_URL"))
if u, err := url.Parse(base); err != nil || !u.IsAbs() {
    return fmt.Errorf("BaseRESTURL %q must be an absolute URL", base)
}
cfg := githubapp.Config{BaseRESTURL: base, ...}
Defensive patterns

Strategy: validation

Validate before calling

func validBaseRESTURL(s string) error {
    u, err := url.Parse(strings.TrimSpace(s))
    if err != nil {
        return err
    }
    if !u.IsAbs() || u.Host == "" {
        return fmt.Errorf("%q must be absolute, e.g. https://api.github.com/", s)
    }
    return nil
}

Prevention

When it happens

Trigger: Config.BaseRESTURL is set to 'api.github.com' (no https:// scheme), to 'https://api.github.com\n' with a trailing newline from an env var, or contains a control character. url.JoinPath(s.cfg.BaseRESTURL, ...) at internal/githubapp/githubapp.go:131 fails during parsing before any HTTP traffic occurs, so Token() fails immediately on the first call.

Common situations: GITHUB_API_URL or the equivalent env var populated by a script that strips the scheme; GitHub Enterprise Server base like 'https://ghe.example.com/api/v3' mistyped as 'ghe.example.com/api/v3'; YAML/JSON config values carrying stray quotes, backslashes, or CRLF.

Related errors


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