git-ecosystem/git-credential-manager · error · ArgumentException

Must specify at least one

Error message

Must specify at least one {nameof(AuthenticationModes)}

What it means

GitLabAuthentication.GetAuthenticationAsync reduces the requested AuthenticationModes (removing unavailable modes like Browser when no desktop session exists) and if nothing remains (modes == AuthenticationModes.None) throws ArgumentException because there is no viable authentication method for the GitLab host.

Solutions

  1. Set credential.gitLabAuthModes (or GCM_GITLAB_AUTHMODES env var) to include an available mode such as Pat (comma-separated list of Browser, Pat, etc.).
  2. Do not pass AuthenticationModes.None when calling the API programmatically.
  3. Run in an environment with a desktop session if you rely on Browser mode.
  4. Provide a PAT so interactive modes can be bypassed entirely (GCM_INTERACTIVE=Never plus stored credential).

Example fix

// before
git config --global credential.gitLabAuthModes browser   // headless: mode stripped -> None

// after
git config --global credential.gitLabAuthModes pat,browser
Defensive patterns

Strategy: validation

Validate before calling

var raw = Environment.GetEnvironmentVariable("GCM_GITLAB_AUTHMODES")
           ?? config["credential.gitLabAuthModes"];
var modes = ParseModes(raw); // parse comma-separated Browser,Pat,...
if (modes == AuthenticationModes.None)
{
    modes = AuthenticationModes.Pat; // ensure at least one viable mode
}
if (!hasDesktopSession) modes &= ~AuthenticationModes.Browser;
if (modes == AuthenticationModes.None) throw new InvalidOperationException("No viable GitLab auth mode configured");

Try / catch

try
{
    var result = await gitLabAuth.GetAuthenticationAsync(targetUri, userName, modes);
}
catch (ArgumentException ex) when (ex.ParamName == "modes")
{
    // retry with a default mode set including Pat
}

Prevention

When it happens

Trigger: Calling GetAuthenticationAsync with AuthenticationModes.None, or with modes that all get stripped (e.g. only Browser requested in a headless session, or PAT disabled by config) leaving None.

Common situations: Configuring credential.gitLabAuthModes to a single mode that is unavailable in the environment (Browser on a server without a desktop session); passing None programmatically; GCM detecting no desktop session so interactive modes are removed.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of git-ecosystem/git-credential-manager@e8ce762cd0 (2026-09-11). Data as JSON: /api/errors/e64f7fdc3371c4e8. Report an issue: GitHub.

Appendix: source

Thrown at src/GitLab/GitLabAuthentication.cs:72

    }

    public class GitLabAuthentication : AuthenticationBase, IGitLabAuthentication
    {
        public GitLabAuthentication(ICommandContext context)
            : base(context) { }

        public async Task<AuthenticationPromptResult> GetAuthenticationAsync(Uri targetUri, string userName, AuthenticationModes modes)
        {
            // If we cannot start a browser then don't offer the option
            if (!Context.SessionManager.IsWebBrowserAvailable)
            {
                modes = modes & ~AuthenticationModes.Browser;
            }

            // We need at least one mode!
            if (modes == AuthenticationModes.None)
            {
                throw new ArgumentException(@$"Must specify at least one {nameof(AuthenticationModes)}", nameof(modes));
            }

            ThrowIfUserInteractionDisabled();

            if (Context.Settings.IsGuiPromptsEnabled && Context.SessionManager.IsDesktopSession)
            {
                if (TryFindHelperCommand(out string helperCommand, out string args))
                {
                    return await GetAuthenticationViaHelperAsync(targetUri, userName, modes, helperCommand, args);
                }

                return await GetAuthenticationViaUiAsync(targetUri, userName, modes);
            }

            return await GetAuthenticationViaTtyAsync(targetUri, userName, modes);
        }

        private async Task<AuthenticationPromptResult> GetAuthenticationViaUiAsync(

View on GitHub (pinned to e8ce762cd0)