github/github-mcp-server · warning

GitHub App ID or client ID is required (GITHUB_APP_ID)

Error message

GitHub App ID or client ID is required (GITHUB_APP_ID)

What it means

Raised when json.Marshal(minimalRepos) fails while serializing the []MinimalRepository projection built from the ListStarred response. Every field is an int64/string or a pre-formatted time string (UpdatedAt is formatted with a fixed layout), all of which always marshal. The error exists as a defensive invariant; Go's encoder would need a NaN/Inf, chan, func, or cyclic value to fail, which this projection cannot contain.

Source

Thrown at internal/githubapp/githubapp.go:53

type Config struct {
	// AppID is used as the JWT issuer. GitHub accepts an app ID or client ID.
	AppID string

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

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Treat as a bug: capture owner/username input and report upstream
  2. Upgrade github-mcp-server to a released version with unmodified DTOs
  3. If maintaining a fork, keep MinimalRepository fields limited to JSON-safe types

Example fix

// before: fork adds an unsupported field
type MinimalRepository struct {
	// ...
	Watcher chan struct{} `json:"watcher"` // marshal fails
}

// after
type MinimalRepository struct {
	// ...
	WatcherCount int `json:"watcher_count"`
}
Defensive patterns

Strategy: try-catch

Type guard

func isMarshalError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "failed to marshal")
}

Try / catch

result, _, err := callListStarredRepositories(ctx, username)
if err != nil && isMarshalError(err) {
	// internal serialization bug: report upstream with the input; not retryable
	log.Printf("marshal bug on starred listing: username=%s err=%v", username, err)
	return err
}

Prevention

When it happens

Trigger: Only reachable if a fork changes MinimalRepository to hold an unmarshalable type (chan, func, *float64 NaN) or a data race corrupts the slice mid-marshal. Stock types decoded and projected from GitHub's API never trigger it.

Common situations: Forked server code adding convenience fields of unsupported types; version skew between a patched go-github and github-mcp-server.

Related errors


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