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

No device authorization endpoint has been configured for…

Error message

No device authorization endpoint has been configured for this client.

What it means

GetDeviceCodeAsync requires a device authorization endpoint to be configured on the client's endpoint collection (_endpoints.DeviceAuthorizationEndpoint). When the OAuth2 client was created without one, the device code flow cannot even be started, so the library throws Trace2InvalidOperationException before making any network call.

Solutions

  1. Configure a valid device authorization endpoint on the client's endpoints before calling GetDeviceCodeAsync (typically from the authority's OIDC discovery document).
  2. Use a modern Azure AD/Entra authority (e.g. https://login.microsoftonline.com/{tenant}) whose discovery document advertises device_authorization_endpoint.
  3. If endpoints are built manually, add the device authorization endpoint (for Entra: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/devicecode).
  4. Fall back to a different interaction mode (webview-based) if the authority does not support device code flow.

Example fix

// before
var endpoints = new OAuth2Endpoints(authorizationEndpoint, tokenEndpoint); // no device endpoint
await client.GetDeviceCodeAsync(endpoints, scopes, ct); // throws
// after
var endpoints = new OAuth2Endpoints(authorizationEndpoint, tokenEndpoint,
    deviceAuthorizationEndpoint: "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/devicecode");
await client.GetDeviceCodeAsync(endpoints, scopes, ct);
Defensive patterns

Strategy: validation

Validate before calling

if (endpoints.DeviceAuthorizationEndpoint is null)
    throw new InvalidOperationException("Device authorization endpoint not configured; cannot use device code flow.");

Try / catch

try
{
    deviceResult = await client.GetDeviceCodeAsync(endpoints, scopes, ct);
}
catch (Trace2InvalidOperationException ex) when (ex.Message.Contains("device authorization endpoint"))
{
    // fall back to a webview-based interaction mode or reconfigure endpoints
    return AuthFailure.NoDeviceEndpoint;
}

Prevention

When it happens

Trigger: Calling GetDeviceCodeAsync on an OAuth2Client whose endpoints (from the OpenID Connect discovery / authority metadata) lack a device_authorization_endpoint — either the endpoints dictionary was built manually without it or the authority's discovery document does not advertise device code support.

Common situations: Pointing the client at a token endpoint or authority whose metadata does not include device_authorization_endpoint (e.g. some ADFS or on-prem STS configurations); constructing OAuth2Endpoints manually with only token/authorization endpoints; an older authority that predates device code flow support.

Understand the failure class

Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.

Related errors


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

Appendix: source

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

            // 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);

            var formData = new Dictionary<string, string>
            {
                [OAuth2Constants.ClientIdParameter] = _clientId
            };

            if (!string.IsNullOrWhiteSpace(scopesStr))
            {
                formData[OAuth2Constants.ScopeParameter] = scopesStr;
            }

            using (HttpContent requestContent = new FormUrlEncodedContent(formData))
            using (HttpRequestMessage request = CreateRequestMessage(HttpMethod.Post, _endpoints.DeviceAuthorizationEndpoint, requestContent))
            using (HttpResponseMessage response = await _httpClient.SendAsync(request, ct))

View on GitHub (pinned to e8ce762cd0)