git-ecosystem/git-credential-manager · error · ArgumentException
Extra query parameter
Error message
Extra query parameter '{kvp.Key}' would override required standard OAuth parameters. What it means
In OAuth2Client.GetAuthorizationCodeAsync, any extra query parameters supplied by the caller are merged into the standard OAuth2 authorization request parameters. If an extra parameter's key collides with a required standard parameter (e.g. client_id, redirect_uri, response_type, scope, state, code_challenge), merging would silently corrupt the flow, so the library throws ArgumentException instead.
Solutions
- Remove keys from extraQueryParams that collide with standard OAuth2 authorization parameters.
- Only pass truly custom/extension parameters (e.g. 'prompt', 'domain_hint', tenant-specific extras) in extraQueryParams.
- Pre-filter the dictionary: drop any key that duplicates client_id, redirect_uri, response_type, scope, state, code_challenge, code_challenge_method before the call.
- Check config that feeds extraQueryParams for full copied URLs and strip their standard query strings.
Example fix
// before
await client.GetAuthorizationCodeAsync(endpoints, clientId, redirectUri, scopes, state, codeChallenge,
new Dictionary<string,string> { ["state"] = "my-state" }); // throws
// after
var extra = new Dictionary<string,string> { ["domain_hint"] = "contoso.com" }; // no standard keys
await client.GetAuthorizationCodeAsync(endpoints, clientId, redirectUri, scopes, state, codeChallenge, extra); Defensive patterns
Strategy: validation
Validate before calling
static readonly HashSet<string> StandardParams = new(StringComparer.Ordinal)
{ "client_id", "redirect_uri", "response_type", "scope", "state", "code_challenge", "code_challenge_method" };
bool conflicts = extra?.Keys.Any(StandardParams.Contains) == true;
if (conflicts) throw new ArgumentException("extraQueryParams may not override standard OAuth parameters."); Try / catch
try
{
result = await client.GetAuthorizationCodeAsync(endpoints, clientId, redirectUri, scopes, state, verifier, extra);
}
catch (ArgumentException ex) when (ex.ParamName == "extraQueryParams")
{
// strip conflicting keys and retry once
var safe = extra.Where(kvp => !StandardParams.Contains(kvp.Key)).ToDictionary(k => k.Key, v => v.Value);
result = await client.GetAuthorizationCodeAsync(endpoints, clientId, redirectUri, scopes, state, verifier, safe);
} Prevention
- Whitelist only known-safe extension parameters in configuration.
- Never paste a full authorization URL's query string into extraQueryParams.
- Keep standard parameters as explicit method arguments, never extras.
- Add a unit test asserting no standard key appears in extras.
When it happens
Trigger: Calling GetAuthorizationCodeAsync with an extraQueryParams dictionary containing a key already present in the computed standard query parameters — for example passing 'redirect_uri', 'state', 'client_id', or 'scope' as an extra parameter.
Common situations: Configuration files that let users append arbitrary OAuth query parameters which accidentally include standard names; copying a full authorization URL's query string into extraQueryParams; SDK or wrapper code forwarding all options blindly.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- Missing ' ' in response.
- Missing ' ' in response.
- OAuth2 response error (from token endpoint response)
- Bitbucket DC OAuth Client ID must be defined
- Bitbucket DC OAuth Client Secret must be defined
AI-assisted analysis of git-ecosystem/git-credential-manager@e8ce762cd0 (2026-09-11).
Data as JSON: /api/errors/3992ef4a207b0b4f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Core/Authentication/OAuth/OAuth2Client.cs:139
[OAuth2Constants.AuthorizationEndpoint.PkceChallengeMethodParameter] =
OAuth2Constants.AuthorizationEndpoint.PkceChallengeMethodS256,
[OAuth2Constants.AuthorizationEndpoint.PkceChallengeParameter] = codeChallenge
};
// Only send the parameter when requesting a non-default mode to keep the request unchanged otherwise.
if (_responseMode != OAuth2ResponseMode.Default)
{
queryParams[OAuth2Constants.AuthorizationEndpoint.ResponseModeParameter] =
_responseMode.GetParameterValue();
}
if (extraQueryParams?.Count > 0)
{
foreach (var kvp in extraQueryParams)
{
if (queryParams.ContainsKey(kvp.Key))
{
throw new ArgumentException(
$"Extra query parameter '{kvp.Key}' would override required standard OAuth parameters.",
nameof(extraQueryParams));
}
queryParams[kvp.Key] = kvp.Value;
}
}
Uri redirectUri = null;
if (_redirectUri != null)
{
redirectUri = browser.UpdateRedirectUri(_redirectUri);
// We must use the .OriginalString property here over .ToString() because OAuth requires the redirect
// URLs to be compared exactly, respecting missing/present trailing slashes, byte-for-byte.
queryParams[OAuth2Constants.RedirectUriParameter] = redirectUri.OriginalString;
}
View on GitHub (pinned to e8ce762cd0)