git-ecosystem/git-credential-manager · error · Trace2InvalidOperationException
Browser authentication requires a desktop session
Error message
Browser authentication requires a desktop session
What it means
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.
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
Example fix
// before var token = await oauth.GetTokenByBrowserAsync(client, scopes); // headless CI // after var token = await oauth.GetTokenByDeviceCodeAsync(client, scopes); // works headless
Defensive patterns
Strategy: type-guard
Validate before calling
if (!context.SessionManager.IsDesktopSession) throw new InvalidOperationException("Browser flow requires a desktop session; use device code flow."); Type guard
bool CanUseBrowserFlow(CommandContext ctx) => ctx.SessionManager.IsDesktopSession;
Try / catch
try { token = await oauth.GetTokenByBrowserAsync(client, scopes); } catch (InvalidOperationException ex) when (ex.Message.Contains("desktop session")) { token = await oauth.GetTokenByDeviceCodeAsync(client, scopes); } Prevention
- Check IsDesktopSession before choosing browser flow
- Prefer device code flow in SSH/CI/container environments
- Cache credentials so interactive auth rarely triggers
When it happens
Trigger: 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.
Common situations: git push/fetch with GCM in an SSH session; CI pipelines needing GitHub credentials; WSL without Windows browser interop configured; running cron/systemd services.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 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/add9a91505f9127c.
Report an issue: GitHub.
Appendix: source
Thrown at src/Core/Authentication/OAuthAuthentication.cs:184
return OAuthAuthenticationModes.Browser;
case "devicecode":
return OAuthAuthenticationModes.DeviceCode;
default:
throw new Trace2Exception(Context.Trace2,
$"Unknown mode value in response '{responseMode}'");
}
}
public async Task<OAuth2TokenResult> GetTokenByBrowserAsync(OAuth2Client client, string[] scopes)
{
ThrowIfUserInteractionDisabled();
// 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);
var authCode = await client.GetAuthorizationCodeAsync(scopes, browser, CancellationToken.None);
return await client.GetTokenByAuthorizationCodeAsync(authCode, CancellationToken.None);
}
public async Task<OAuth2TokenResult> GetTokenByDeviceCodeAsync(OAuth2Client client, string[] scopes)
{
ThrowIfUserInteractionDisabled();
OAuth2DeviceCodeResult dcr = await client.GetDeviceCodeAsync(scopes, CancellationToken.None);
// If we have a desktop session show the device code in a dialog
if (Context.Settings.IsGuiPromptsEnabled && Context.SessionManager.IsDesktopSession)
{View on GitHub (pinned to e8ce762cd0)