{"record":{"id":"6f43568e2911a0dd","repo":"git-ecosystem/git-credential-manager","slug":"browser-authentication-requires-a-desktop-session-6f4356","errorCode":null,"errorMessage":"Browser authentication requires a desktop session","messagePattern":"Browser authentication requires a desktop session","errorType":"exception","errorClass":"Trace2InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/GitLab/GitLabAuthentication.cs","lineNumber":270,"sourceCode":"                    return new AuthenticationPromptResult(\n                        AuthenticationModes.Basic, new GitCredential(userName, password));\n\n                default:\n                    throw new Trace2Exception(Context.Trace2,\n                        $\"Unknown mode value in response '{responseMode}'\");\n            }\n        }\n\n        public async Task<OAuth2TokenResult> GetOAuthTokenViaBrowserAsync(Uri targetUri, IEnumerable<string> scopes)\n        {\n            ThrowIfUserInteractionDisabled();\n\n            var oauthClient = new GitLabOAuth2Client(HttpClient, Context.Settings, targetUri, Context.Trace2);\n\n            // We require a desktop session to launch the user's default web browser\n            if (!Context.SessionManager.IsDesktopSession)\n            {\n                throw new Trace2InvalidOperationException(Context.Trace2,\n                    \"Browser authentication requires a desktop session\");\n            }\n\n            var browserOptions = new OAuth2WebBrowserOptions { };\n            var browser = new OAuth2SystemWebBrowser(Context.SessionManager, browserOptions);\n\n            // Write message to the terminal (if any is attached) for some feedback that we're waiting for a web response\n            Context.Console.WriteInfo(\"please complete authentication in your browser...\");\n\n            OAuth2AuthorizationCodeResult authCodeResult =\n                await oauthClient.GetAuthorizationCodeAsync(scopes, browser, CancellationToken.None);\n\n            return await oauthClient.GetTokenByAuthorizationCodeAsync(authCodeResult, CancellationToken.None);\n        }\n\n        public async Task<OAuth2TokenResult> GetOAuthTokenViaRefresh(Uri targetUri, string refreshToken)\n        {\n            var oauthClient = new GitLabOAuth2Client(HttpClient, Context.Settings, targetUri, Context.Trace2);","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/git-ecosystem/git-credential-manager/blob/e8ce762cd04b4100ae637b5fbf39ef9d0a96561e/src/GitLab/GitLabAuthentication.cs#L252-L288","documentation":"GetOAuthTokenViaBrowserAsync launches the user's default web browser for the GitLab OAuth2 flow, which is only possible in an interactive desktop session. When Context.SessionManager.IsDesktopSession is false, this Trace2InvalidOperationException is thrown instead of hanging or failing to open a browser.","triggerScenarios":"Calling GetOAuthTokenViaBrowserAsync (or a GitLab authentication flow that requests browser mode) from an SSH session, Windows service, CI pipeline, container, or headless cron job where no desktop session exists.","commonSituations":"Running git push with GitLab browser auth over SSH to a server; GitHub Actions/GitLab CI runners without GCM_INTERACTIVE or device-flow support; Docker containers doing git operations.","solutions":["Use a PAT instead of browser authentication (credential.gitLabAuthModes pat or `git-credential-manager gitlab login --pat`).","Run the authentication step on a machine with a desktop session, then copy/transport the stored credential.","If on SSH, port-forward and use an environment that can open a local browser, or set GCM to non-browser modes."],"exampleFix":"// before (CI)\ngit-credential-manager gitlab login\n// after\ngit-credential-manager gitlab login --pat\n# or set\ngit config --global credential.gitLabAuthModes pat","handlingStrategy":"fallback","validationCode":"// Check desktop session before requesting browser auth\nif (!Environment.UserInteractive || string.IsNullOrEmpty(Environment.GetEnvironmentVariable(\"DISPLAY\")) && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {\n  // use PAT flow instead of browser flow\n}","typeGuard":null,"tryCatchPattern":"try {\n  var token = await auth.GetOAuthTokenViaBrowserAsync(targetUri, scopes);\n} catch (InvalidOperationException ex) when (ex.Message.Contains(\"desktop session\")) {\n  // fall back to PAT-based authentication\n  var pat = await GetPatAsync();\n}","preventionTips":["In CI/SSH/headless environments, configure PAT authentication up front","Set credential.gitLabAuthModes to exclude browser on non-interactive hosts","Authenticate once on a desktop machine and reuse the stored credential"],"tags":["gitlab","oauth","browser","headless"],"backgroundTag":"unsupported-platform","analyzedSha":"e8ce762cd04b4100ae637b5fbf39ef9d0a96561e","analyzedAt":"2026-09-11T17:15:08.753Z","contentChangedAt":"2026-09-11T17:15:08.753Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}