git-ecosystem/git-credential-manager · error · Trace2InvalidOperationException
Browser authentication requires a desktop session
Error message
Browser authentication requires a desktop session
What it means
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.
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.
Example fix
// before (CI) git-credential-manager gitlab login // after git-credential-manager gitlab login --pat # or set git config --global credential.gitLabAuthModes pat
Defensive patterns
Strategy: fallback
Validate before calling
// Check desktop session before requesting browser auth
if (!Environment.UserInteractive || string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DISPLAY")) && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
// use PAT flow instead of browser flow
} Try / catch
try {
var token = await auth.GetOAuthTokenViaBrowserAsync(targetUri, scopes);
} catch (InvalidOperationException ex) when (ex.Message.Contains("desktop session")) {
// fall back to PAT-based authentication
var pat = await GetPatAsync();
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Browser authentication requires a desktop session
- Failed to resolve username. HTTP
- helper error ( )
- Must specify at least one OAuthAuthenticationModes
- Missing 'mode' in response
AI-assisted analysis of git-ecosystem/git-credential-manager@e8ce762cd0 (2026-09-11).
Data as JSON: /api/errors/6f43568e2911a0dd.
Report an issue: GitHub.
Appendix: source
Thrown at src/GitLab/GitLabAuthentication.cs:270
return new AuthenticationPromptResult(
AuthenticationModes.Basic, new GitCredential(userName, password));
default:
throw new Trace2Exception(Context.Trace2,
$"Unknown mode value in response '{responseMode}'");
}
}
public async Task<OAuth2TokenResult> GetOAuthTokenViaBrowserAsync(Uri targetUri, IEnumerable<string> scopes)
{
ThrowIfUserInteractionDisabled();
var oauthClient = new GitLabOAuth2Client(HttpClient, Context.Settings, targetUri, Context.Trace2);
// We require a desktop session to launch the user's default web browser
if (!Context.SessionManager.IsDesktopSession)
{
throw new Trace2InvalidOperationException(Context.Trace2,
"Browser authentication requires a desktop session");
}
var browserOptions = new OAuth2WebBrowserOptions { };
var browser = new OAuth2SystemWebBrowser(Context.SessionManager, browserOptions);
// Write message to the terminal (if any is attached) for some feedback that we're waiting for a web response
Context.Console.WriteInfo("please complete authentication in your browser...");
OAuth2AuthorizationCodeResult authCodeResult =
await oauthClient.GetAuthorizationCodeAsync(scopes, browser, CancellationToken.None);
return await oauthClient.GetTokenByAuthorizationCodeAsync(authCodeResult, CancellationToken.None);
}
public async Task<OAuth2TokenResult> GetOAuthTokenViaRefresh(Uri targetUri, string refreshToken)
{
var oauthClient = new GitLabOAuth2Client(HttpClient, Context.Settings, targetUri, Context.Trace2);View on GitHub (pinned to e8ce762cd0)