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

Missing ' ' in response.

Error message

Missing '{OAuth2Constants.AuthorizationGrantResponse.AuthorizationCodeParameter}' in response.

What it means

After validating state, GetAuthorizationCodeAsync requires the authorization 'code' parameter in the response. If the response parameters contain no authorization code the flow is terminated with this Trace2OAuth2Exception — the authorization server did not issue (or did not echo back) a code, so no token exchange is possible.

Solutions

  1. Check the response parameters for OAuth error fields (error, error_description) and surface them to the user before retrying.
  2. Ensure the complete redirect URL (query string intact) is captured and passed to GetAuthorizationCodeAsync.
  3. Prompt the user to retry the sign-in — a missing code often means the login/consent was not completed.
  4. Verify the redirect URI is registered correctly with the authorization server so it actually issues a code to your client.

Example fix

// before
var result = await client.GetAuthorizationCodeAsync(endpoints, clientId, redirectUri, scopes, state, verifier,
    new Uri("https://client/callback?error=access_denied")); // no code
// after
if (responseParams.ContainsKey("error"))
    throw new Exception($"Auth failed: {responseParams["error"]}");
var result = await client.GetAuthorizationCodeAsync(endpoints, clientId, redirectUri, scopes, state, verifier, successCallbackUrl);
Defensive patterns

Strategy: try-catch

Validate before calling

var responseParams = ParseResponseParams(callbackUrl);
if (responseParams.TryGetValue("error", out var err))
    throw new InvalidOperationException($"Authorization failed: {err}");
if (!responseParams.ContainsKey("code"))
    throw new InvalidOperationException("Authorization response is missing 'code'.");

Try / catch

try
{
    result = await client.GetAuthorizationCodeAsync(endpoints, clientId, redirectUri, scopes, state, verifier, callbackUrl);
}
catch (Trace2OAuth2Exception ex) when (ex.Message.Contains("AuthorizationCodeParameter"))
{
    // no auth code: user likely denied consent or login failed; surface error and allow retry
    return AuthFailure.NoAuthorizationCode;
}

Prevention

When it happens

Trigger: Calling GetAuthorizationCodeAsync with a response URL/parameters that lack the 'code' parameter — e.g. the user denied consent at the IdP and the redirect carried only an error, or the response URL was truncated/mangled before parsing.

Common situations: User cancelled the consent screen or login failed and the IdP redirected back with an error instead of a code; a local redirect listener dropped query parameters; proxy or URL-encoding issues stripped the code.

Related errors


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

Appendix: source

Thrown at src/Core/Authentication/OAuth/OAuth2Client.cs:193

            // Check for errors serious enough we should terminate the flow, such as if the state value returned does
            // not match the one we passed. This indicates a badly implemented Authorization Server, or worse, some
            // form of failed MITM or replay attack.
            if (!responseParams.TryGetValue(OAuth2Constants.AuthorizationGrantResponse.StateParameter, out string replyState))
            {
                throw new Trace2OAuth2Exception(_trace2,
                    $"Missing '{OAuth2Constants.AuthorizationGrantResponse.StateParameter}' in response.");
            }
            if (!StringComparer.Ordinal.Equals(state, replyState))
            {
                throw new Trace2OAuth2Exception(_trace2,
                    $"Invalid '{OAuth2Constants.AuthorizationGrantResponse.StateParameter}' in response; does not match the request.");
            }

            // We expect to have the auth code in the response otherwise terminate the flow (we failed authentication for some reason)
            if (!responseParams.TryGetValue(OAuth2Constants.AuthorizationGrantResponse.AuthorizationCodeParameter, out string authCode))
            {
                throw new Trace2OAuth2Exception(_trace2,
                    $"Missing '{OAuth2Constants.AuthorizationGrantResponse.AuthorizationCodeParameter}' in response.");
            }

            return new OAuth2AuthorizationCodeResult(authCode, redirectUri, codeVerifier);
        }

        public async Task<OAuth2DeviceCodeResult> GetDeviceCodeAsync(IEnumerable<string> scopes, CancellationToken ct)
        {
            var label = "get device code";
            using IDisposable region = _trace2.CreateRegion(OAuth2Constants.Trace2Category, label);

            if (_endpoints.DeviceAuthorizationEndpoint is null)
            {
                throw new Trace2InvalidOperationException(_trace2,
                    "No device authorization endpoint has been configured for this client.");
            }

            string scopesStr = string.Join(" ", scopes);

View on GitHub (pinned to e8ce762cd0)