{"record":{"id":"2c9ffa071b10ecb8","repo":"github/github-mcp-server","slug":"authorization-failed-s","errorCode":null,"errorMessage":"authorization failed: %s","messagePattern":"authorization failed: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/oauth/callback.go","lineNumber":91,"sourceCode":"\t\t}\n\t}()\n\n\treturn cs\n}\n\n// handler renders the callback endpoint. It reports the outcome exactly once and\n// always shows the user a friendly page.\nfunc (cs *callbackServer) handler(expectedState string) http.Handler {\n\tmux := http.NewServeMux()\n\tmux.HandleFunc(\"/callback\", func(w http.ResponseWriter, r *http.Request) {\n\t\tq := r.URL.Query()\n\n\t\tif errCode := q.Get(\"error\"); errCode != \"\" {\n\t\t\tmsg := errCode\n\t\t\tif desc := q.Get(\"error_description\"); desc != \"\" {\n\t\t\t\tmsg = fmt.Sprintf(\"%s: %s\", errCode, desc)\n\t\t\t}\n\t\t\tcs.report(callbackResult{err: fmt.Errorf(\"authorization failed: %s\", msg)})\n\t\t\trenderError(w, msg)\n\t\t\treturn\n\t\t}\n\n\t\tif q.Get(\"state\") != expectedState {\n\t\t\tcs.report(callbackResult{err: fmt.Errorf(\"state mismatch (possible CSRF)\")})\n\t\t\trenderError(w, \"state mismatch\")\n\t\t\treturn\n\t\t}\n\n\t\tcode := q.Get(\"code\")\n\t\tif code == \"\" {\n\t\t\tcs.report(callbackResult{err: fmt.Errorf(\"no authorization code in callback\")})\n\t\t\trenderError(w, \"no authorization code received\")\n\t\t\treturn\n\t\t}\n\n\t\tcs.report(callbackResult{code: code})","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/internal/oauth/callback.go#L73-L109","documentation":"The OAuth callback arrived with an error query parameter, meaning GitHub itself refused or the user aborted the authorization. The message combines error and error_description per RFC 6749 §4.1.2.1 (e.g. 'access_denied: The user has denied your application access'). The handler renders an error page AND records the failure via cs.report at internal/oauth/callback.go:79-86, so the pending flow fails.","triggerScenarios":"User clicks 'Cancel'/'Deny' on the GitHub consent screen (error=access_denied); the app requests scopes it is not approved for (error=invalid_scope); the OAuth app is suspended by GitHub (redirect with an error); SAML enforcement bounces the callback with an error param.","commonSituations":"A cautious user denying the consent dialog; org policy changes revoking the app mid-flow; app suspended or deleted while a user had the auth page open; OAuth app scope list edited between link generation and consent.","solutions":["If error=access_denied, the user declined — re-run login and approve, or use a different account","For invalid_scope, trim the requested scopes to ones the GitHub App/OAuth app is approved to request","Check the app's status on GitHub (Settings -> Developer settings) — suspended apps produce errors on every redirect","Regenerate the authorization URL by retrying; stale URLs from before an app change fail this way"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if strings.Contains(err.Error(), \"authorization failed:\") {\n    // surface error_description to the user; access_denied is a user choice, not a bug\n}","preventionTips":["Request only scopes the app is approved for","Keep the app active (unsuspended) on GitHub and re-issue login links after any app configuration change"],"tags":["oauth","authorization","user-action","github-api"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}