github/github-mcp-server · warning

GitHub App private key is required (GITHUB_APP_PRIVATE_KEY_P

Error message

GitHub App private key is required (GITHUB_APP_PRIVATE_KEY_PATH or GITHUB_APP_PRIVATE_KEY)

What it means

Activity.Star returns 204 No Content on success; on any other status (404 repo not found, 401, 403) the handler reads resp.Body with io.ReadAll to build the status error. This error means that read failed - a transport fault after the status line arrived, which discards the real API error message. Mid-body connection resets, proxy stream closes, and consumed bodies are the usual causes.

Source

Thrown at internal/githubapp/githubapp.go:57

	// InstallationID identifies the installation whose access token is minted.
	InstallationID string

	// PrivateKeyPEM is the RSA key used to sign app JWTs.
	PrivateKeyPEM []byte

	// BaseRESTURL is the REST API base, e.g. https://api.github.com/ for
	// github.com or https://HOST/api/v3/ for GitHub Enterprise Server.
	BaseRESTURL string
}

func (c Config) validate() error {
	switch {
	case c.AppID == "":
		return errors.New("GitHub App ID or client ID is required (GITHUB_APP_ID)")
	case c.InstallationID == "":
		return errors.New("GitHub App installation ID is required (GITHUB_APP_INSTALLATION_ID)")
	case len(c.PrivateKeyPEM) == 0:
		return errors.New("GitHub App private key is required (GITHUB_APP_PRIVATE_KEY_PATH or GITHUB_APP_PRIVATE_KEY)")
	case c.BaseRESTURL == "":
		return errors.New("GitHub App REST base URL is required")
	}
	return nil
}

func parsePrivateKey(pemBytes []byte) (*rsa.PrivateKey, error) {
	block, _ := pem.Decode(pemBytes)
	if block == nil {
		return nil, errors.New("no PEM block found in private key")
	}
	if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
		return key, nil
	}
	parsed, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("parsing private key (want PKCS#1 or PKCS#8 RSA): %w", err)
	}

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Retry star_repository once - starring is idempotent (204 whether newly starred or already starred)
  2. Verify owner/repo spelling separately, since 404 is the most common non-204 cause
  3. Stabilize proxy/LB idle timeouts if the failure recurs
  4. Log the pre-read status code to separate API errors from transport drops

Example fix

// before: single attempt fails at body read
err := starRepo(ctx, owner, repo)

// after: bounded retry; star is idempotent so a repeat is safe
err := starRepo(ctx, owner, repo)
if isBodyReadError(err) {
	time.Sleep(250 * time.Millisecond)
	err = starRepo(ctx, owner, repo)
}
Defensive patterns

Strategy: retry

Type guard

func isBodyReadError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "failed to read response body")
}

Try / catch

// star_repository is idempotent (204 whether newly starred or not).
err := starRepo(ctx, owner, repo)
if isBodyReadError(err) {
	time.Sleep(250 * time.Millisecond)
	err = starRepo(ctx, owner, repo)
}

Prevention

When it happens

Trigger: Star replies 404 (owner/repo does not exist) and the connection drops before the error body finishes; a proxy resets the stream; keep-alive race between status and body.

Common situations: Scripted starring loops over flaky networks; VPN/proxy instability; LB connection reaping under load.

Related errors


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