{"record":{"id":"6b86325ebc795156","repo":"github/github-mcp-server","slug":"no-authorization-code-in-callback","errorCode":null,"errorMessage":"no authorization code in callback","messagePattern":"no authorization code in callback","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/oauth/callback.go","lineNumber":104,"sourceCode":"\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})\n\t\trenderSuccess(w)\n\t})\n\treturn mux\n}\n\n// report delivers the first outcome and drops later ones (the channel is\n// buffered for one; subsequent redirect retries must not block the handler).\nfunc (cs *callbackServer) report(res callbackResult) {\n\tselect {\n\tcase cs.results <- res:\n\tdefault:\n\t}\n}","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/github/github-mcp-server/blob/0ea1f775a7c73eff1bd2e25904d01136756bbfe2/internal/oauth/callback.go#L86-L122","documentation":"The callback request reached the server with the right shape (no error param, state matched) but carried no code query parameter, so there is nothing to exchange. Reported at internal/oauth/callback.go:103-106 after the empty-string check on q.Get(\"code\"). The user sees the friendly page 'no authorization code received' while the flow records the precise error.","triggerScenarios":"User navigates directly to http://localhost:PORT/callback by hand; a browser extension or prefetcher fetches the redirect URI without GitHub's parameters; a health-check/probe hits /callback; GitHub redirects with state but the code parameter is stripped by an over-aggressive URL cleaner or security product on the machine.","commonSituations":"Curious user typing the callback URL into the browser; antivirus/web-filter stripping unknown query params from localhost URLs; an old bookmark to the callback page; test suites probing the port; SSRF-ish scanners inside a container network hitting the published callback port.","solutions":["Always reach /callback only via GitHub's redirect — open the authorization URL shown in the prompt and complete consent","Disable URL 'cleaning' features of extensions/security software for localhost","If a probe/monitor causes it, exclude the callback port from health checks","Retry the login flow — a clean attempt delivers code the first time"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"if strings.Contains(err.Error(), \"no authorization code in callback\") {\n    // benign probe or param stripping: instruct the user to restart and use the redirect only\n}","preventionTips":["Exclude the callback port from health checks and monitoring probes","Disable URL-parameter stripping in local security software/extensions for localhost"],"tags":["oauth","callback","user-action","http"],"backgroundTag":null,"analyzedSha":"0ea1f775a7c73eff1bd2e25904d01136756bbfe2","analyzedAt":"2026-08-15T18:10:19.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}