github/github-mcp-server · error

decoding installation token response: %w

Error message

decoding installation token response: %w

What it means

The endpoint returned 201 Created but the JSON decoder could not parse the body: the response is not valid JSON matching {token, expires_at}. Since genuine GitHub responses are well-formed JSON, this points at an intercepting proxy returning HTML, a GHES misconfiguration, or a body truncated mid-transfer. The %w wrap exposes the encoding/json error (e.g. 'invalid character < looking for beginning of value').

Source

Thrown at internal/githubapp/githubapp.go:166

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

	var body struct {
		Token     string    `json:"token"`
		ExpiresAt time.Time `json:"expires_at"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
		return nil, fmt.Errorf("decoding installation token response: %w", err)
	}
	if body.Token == "" {
		return nil, errors.New("installation token response did not contain a token")
	}
	if body.ExpiresAt.IsZero() {
		return nil, errors.New("installation token response did not contain an expiry")
	}
	return &oauth2.Token{
		AccessToken: body.Token,
		TokenType:   "token",
		Expiry:      body.ExpiresAt.Add(-refreshBuffer),
	}, nil
}

// Provider caches and refreshes GitHub App installation access tokens.
type Provider struct {
	source oauth2.TokenSource
	logger *slog.Logger

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Read the wrapped json error: 'invalid character <' means HTML (proxy/captive portal) — inspect what actually answers with curl -k -v
  2. Set NO_PROXY for the API host or fix HTTPS_PROXY so the GitHub endpoint is not MITM'd
  3. For GHES, confirm BaseRESTURL is exactly https://HOST/api/v3/ and the appliance version is current
  4. Retry on 'unexpected EOF' — truncation is often transient
Defensive patterns

Strategy: try-catch

Try / catch

if strings.Contains(err.Error(), "invalid character") {
    // HTML body: a proxy/captive portal answered — inspect with curl, fix proxy config
}

Prevention

When it happens

Trigger: A captive portal / corporate MITM proxy answers 201 with an HTML page; a GHES appliance returns a non-JSON success payload for the api/v3 path; a body larger than the connection's content-length that got cut, causing 'unexpected EOF' in json.NewDecoder(...).Decode at internal/githubapp/githubapp.go:165.

Common situations: Guest/hotel Wi-Fi captive portal intercepting HTTPS via an installed trust root; an internal API gateway fronting GHES that rewrites responses; flaky links truncating bodies; HTTP/2 downgrade middleboxes corrupting framing.

Related errors


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