{"record":{"id":"add9a91505f9127c","repo":"git-ecosystem/git-credential-manager","slug":"browser-authentication-requires-a-desktop-session","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/Core/Authentication/OAuthAuthentication.cs","lineNumber":184,"sourceCode":"                    return OAuthAuthenticationModes.Browser;\n\n                case \"devicecode\":\n                    return OAuthAuthenticationModes.DeviceCode;\n\n                default:\n                    throw new Trace2Exception(Context.Trace2,\n                        $\"Unknown mode value in response '{responseMode}'\");\n            }\n        }\n\n        public async Task<OAuth2TokenResult> GetTokenByBrowserAsync(OAuth2Client client, string[] scopes)\n        {\n            ThrowIfUserInteractionDisabled();\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            var authCode = await client.GetAuthorizationCodeAsync(scopes, browser, CancellationToken.None);\n            return await client.GetTokenByAuthorizationCodeAsync(authCode, CancellationToken.None);\n        }\n\n        public async Task<OAuth2TokenResult> GetTokenByDeviceCodeAsync(OAuth2Client client, string[] scopes)\n        {\n            ThrowIfUserInteractionDisabled();\n\n            OAuth2DeviceCodeResult dcr = await client.GetDeviceCodeAsync(scopes, CancellationToken.None);\n\n            // If we have a desktop session show the device code in a dialog\n            if (Context.Settings.IsGuiPromptsEnabled && Context.SessionManager.IsDesktopSession)\n            {","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/git-ecosystem/git-credential-manager/blob/e8ce762cd04b4100ae637b5fbf39ef9d0a96561e/src/Core/Authentication/OAuthAuthentication.cs#L166-L202","documentation":"GetTokenByBrowserAsync first checks that the current session is a desktop session, because browser-based OAuth requires launching the user's default web browser. Without a desktop session it throws Trace2InvalidOperationException.","triggerScenarios":"Calling GetTokenByBrowserAsync (or triggering browser-mode OAuth) over SSH, in a headless service/CI job, in a container without a display, or on Windows over a non-interactive session where SessionManager.IsDesktopSession is false.","commonSituations":"git push/fetch with GCM in an SSH session; CI pipelines needing GitHub credentials; WSL without Windows browser interop configured; running cron/systemd services.","solutions":["Use device code flow instead of browser flow for headless environments","Run the command inside an interactive desktop session","Configure GCM to use a credential helper / cached credential instead of interactive auth","For WSL, ensure browser interop (e.g. wslview/BROWSER) is available so a desktop session is detected"],"exampleFix":"// before\nvar token = await oauth.GetTokenByBrowserAsync(client, scopes); // headless CI\n// after\nvar token = await oauth.GetTokenByDeviceCodeAsync(client, scopes); // works headless","handlingStrategy":"type-guard","validationCode":"if (!context.SessionManager.IsDesktopSession) throw new InvalidOperationException(\"Browser flow requires a desktop session; use device code flow.\");","typeGuard":"bool CanUseBrowserFlow(CommandContext ctx) => ctx.SessionManager.IsDesktopSession;","tryCatchPattern":"try { token = await oauth.GetTokenByBrowserAsync(client, scopes); } catch (InvalidOperationException ex) when (ex.Message.Contains(\"desktop session\")) { token = await oauth.GetTokenByDeviceCodeAsync(client, scopes); }","preventionTips":["Check IsDesktopSession before choosing browser flow","Prefer device code flow in SSH/CI/container environments","Cache credentials so interactive auth rarely triggers"],"tags":["oauth","browser","desktop-session","headless"],"backgroundTag":"unsupported-operation","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"}