github/github-mcp-server · error · mark.ErrBadRequest

%w: missing required Authorization header

Error message

%w: missing required Authorization header

What it means

Sentinel error returned by utils.ParseAuthorizationHeader when the request carries no Authorization header at all. Although it wraps mark.ErrBadRequest ('bad request'), the token middleware special-cases it: instead of a 400 it calls sendAuthChallenge, producing a 401 with a WWW-Authenticate Bearer challenge per the MCP authorization spec so OAuth clients start the flow.

Source

Thrown at pkg/utils/token.go:33

const (
	TokenTypeUnknown TokenType = iota
	TokenTypePersonalAccessToken
	TokenTypeFineGrainedPersonalAccessToken
	TokenTypeOAuthAccessToken
	TokenTypeUserToServerGitHubAppToken
	TokenTypeServerToServerGitHubAppToken
)

var supportedGitHubPrefixes = map[string]TokenType{
	"ghp_":        TokenTypePersonalAccessToken,            // Personal access token (classic)
	"github_pat_": TokenTypeFineGrainedPersonalAccessToken, // Fine-grained personal access token
	"gho_":        TokenTypeOAuthAccessToken,               // OAuth access token
	"ghu_":        TokenTypeUserToServerGitHubAppToken,     // User access token for a GitHub App
	"ghs_":        TokenTypeServerToServerGitHubAppToken,   // Installation access token for a GitHub App (a.k.a. server-to-server token)
}

var (
	ErrMissingAuthorizationHeader     = fmt.Errorf("%w: missing required Authorization header", mark.ErrBadRequest)
	ErrBadAuthorizationHeader         = fmt.Errorf("%w: Authorization header is badly formatted", mark.ErrBadRequest)
	ErrUnsupportedAuthorizationHeader = fmt.Errorf("%w: unsupported Authorization header", mark.ErrBadRequest)
)

// oldPatternRegexp is the regular expression for the old pattern of the token.
// Until 2021, GitHub API tokens did not have an identifiable prefix. They
// were 40 characters long and only contained the characters a-f and 0-9.
var oldPatternRegexp = regexp.MustCompile(`\A[a-f0-9]{40}\z`)

// ParseAuthorizationHeader parses the Authorization header from the HTTP request
func ParseAuthorizationHeader(req *http.Request) (tokenType TokenType, token string, _ error) {
	authHeader := req.Header.Get(httpheaders.AuthorizationHeader)
	if authHeader == "" {
		return 0, "", ErrMissingAuthorizationHeader
	}

	switch {
	// decrypt dotcom token and set it as token

View on GitHub (pinned to 0ea1f775a7)

Solutions

  1. Send Authorization: Bearer <GitHub token> on every request to protected endpoints
  2. As an OAuth client, treat the 401 + WWW-Authenticate response as the trigger to run the OAuth flow, then retry
  3. Check intermediaries (nginx auth_request, meshes, gateways) are not stripping Authorization

Example fix

# before
curl http://localhost:8080/api/mcp -d @req.json

# after
curl http://localhost:8080/api/mcp -H "Authorization: Bearer ghp_xxxx" -d @req.json
Defensive patterns

Strategy: validation

Validate before calling

// client side, before sending
if token == "" {
	return errors.New("no token configured: the server will answer 401 with a Bearer challenge")
}
req.Header.Set("Authorization", "Bearer "+token)

Type guard

func isMissingAuthHeader(err error) bool {
	return errors.Is(err, utils.ErrMissingAuthorizationHeader)
}

Try / catch

if err != nil {
	if errors.Is(err, utils.ErrMissingAuthorizationHeader) {
		// middleware already sent 401 + WWW-Authenticate: start/continue the OAuth flow, then retry
	} else {
		// 400-class header problems: fix the token, do not challenge
	}
}

Prevention

When it happens

Trigger: Any HTTP request to a token-protected endpoint without an Authorization header: bare curl, an MCP client with no token configured, or a proxy that strips the header.

Common situations: First requests from remote MCP clients before OAuth completes; curl tests forgetting -H 'Authorization: Bearer ...'; reverse proxies or service meshes dropping the header.

Related errors


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