github/github-mcp-server · error · utils.ErrBadAuthorizationHeader

bad request: Authorization header is badly formatted

Error message

bad request: Authorization header is badly formatted

What it means

This is the 400 response the token middleware writes via http.Error(w, err.Error(), 400) when ParseAuthorizationHeader fails with a non-missing-header problem and the rendered text is 'bad request: Authorization header is badly formatted' - the mark.ErrBadRequest prefix ('bad request') plus the sentinel from pkg/utils/token.go. Concretely: the header was present, but the token matched no supported GitHub format (ghp_, github_pat_, gho_, ghu_, ghs_, or legacy 40-hex).

Source

Thrown at pkg/http/middleware/token.go:34

			ctx := r.Context()

			// Check if token info already exists in context, if it does, skip extraction.
			// In remote setup, we may have already extracted token info earlier.
			if _, ok := ghcontext.GetTokenInfo(ctx); ok {
				// Token info already exists in context, skip extraction
				next.ServeHTTP(w, r)
				return
			}

			tokenType, token, err := utils.ParseAuthorizationHeader(r)
			if err != nil {
				// For missing Authorization header, return 401 with WWW-Authenticate header per MCP spec
				if errors.Is(err, utils.ErrMissingAuthorizationHeader) {
					sendAuthChallenge(w, r, oauthCfg)
					return
				}
				// For other auth errors (bad format, unsupported), return 400
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}

			ctx = ghcontext.WithTokenInfo(ctx, &ghcontext.TokenInfo{
				Token:     token,
				TokenType: tokenType,
			})
			r = r.WithContext(ctx)

			next.ServeHTTP(w, r)
		})
	}
}

// sendAuthChallenge sends a 401 Unauthorized response with WWW-Authenticate header
// containing the OAuth protected resource metadata URL as per RFC 6750 and MCP spec.
func sendAuthChallenge(w http.ResponseWriter, r *http.Request, oauthCfg *oauth.Config) {
	resourcePath := oauth.ResolveResourcePath(r, oauthCfg)

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Log a safe prefix (first 4 chars) of the token being sent to confirm which value reaches the server
  2. Replace with a supported GitHub token type (ghp_/github_pat_/gho_/ghu_/ghs_)
  3. Verify secret interpolation - an unexpanded ${VAR} or $VAR literal in the value is a classic cause

Example fix

# before
Authorization: Bearer ${GITHUB_TOKEN}   # unexpanded placeholder

# after
Authorization: Bearer ghp_xxxx           # properly interpolated real token
Defensive patterns

Strategy: validation

Validate before calling

var gitHubTokenRe = regexp.MustCompile(`^(ghp_|github_pat_|gho_|ghu_|ghs_)[A-Za-z0-9_]+$|^[a-f0-9]{40}$`)

if !gitHubTokenRe.MatchString(token) {
	return errors.New("token does not look like a GitHub token; server will answer 400 badly formatted")
}
req.Header.Set("Authorization", "Bearer "+token)

Type guard

func isBadAuthHeader(err error) bool {
	return errors.Is(err, utils.ErrBadAuthorizationHeader)
}

Try / catch

// middleware shape: distinguish the three auth sentinels before choosing a status
if err != nil {
	switch {
	case errors.Is(err, utils.ErrMissingAuthorizationHeader):
		sendAuthChallenge(w, r) // 401 + WWW-Authenticate
	case errors.Is(err, utils.ErrBadAuthorizationHeader), errors.Is(err, utils.ErrUnsupportedAuthorizationHeader):
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
}

Prevention

When it happens

Trigger: Authorization: Bearer <token> where the token is a placeholder, another vendor's credential, an unexpanded ${GITHUB_TOKEN} literal, or otherwise prefix-unknown.

Common situations: Example tokens left in committed configs; env interpolation failing in CI so the literal placeholder is sent; tokens truncated by column limits when copied out of logs/secrets.

Related errors


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