git-ecosystem/git-credential-manager · warning · Trace2Exception

User canceled device code authentication

Error message

User canceled device code authentication

What it means

While waiting for the device code token exchange, GetTokenByDeviceCodeAsync catches OperationCanceledException from the pending token task and rethrows it as a Trace2Exception stating the user canceled device code authentication. This distinguishes deliberate cancellation from polling timeout or failure.

Solutions

  1. Retry the device code flow and complete authorization before the code expires
  2. Do not close the device-code prompt dialog until authorization finishes
  3. In automation, avoid canceling the tokenTask's CancellationToken prematurely
  4. Handle this exception as a benign user-abort rather than a system failure

Example fix

// before
tokenResult = await oauth.GetTokenByDeviceCodeAsync(client, scopes);
// after
try
{
    tokenResult = await oauth.GetTokenByDeviceCodeAsync(client, scopes);
}
catch (Exception ex) when (ex.Message.Contains("User canceled"))
{
    // user aborted; fall back to device code retry or another flow
}
Defensive patterns

Strategy: try-catch

Try / catch

try { token = await oauth.GetTokenByDeviceCodeAsync(client, scopes); } catch (Exception ex) when (ex.Message.Contains("User canceled device code authentication")) { /* treat as user abort: retry or return null */ }

Prevention

When it happens

Trigger: The user dismisses the device-code prompt dialog (promptCts/the dialog's cancel), causing the tokenTask's CancellationToken to fire and the awaited token request to be canceled.

Common situations: User closes the 'enter this code' dialog because they changed their mind or the code expired; canceling the GUI prompt in a desktop session; automation killing the prompt window.

Understand the failure class

Related errors


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

Appendix: source

Thrown at src/Core/Authentication/OAuthAuthentication.cs:229

                // Start the request for an OAuth token but don't wait
                Task<OAuth2TokenResult> tokenTask = client.GetTokenByDeviceCodeAsync(dcr, tokenCts.Token);

                Task t = await Task.WhenAny(promptTask, tokenTask);

                // If the dialog was closed the user wishes to cancel the request
                if (t == promptTask)
                {
                    tokenCts.Cancel();
                }

                OAuth2TokenResult tokenResult;
                try
                {
                    tokenResult = await tokenTask;
                }
                catch (OperationCanceledException)
                {
                    throw new Trace2Exception(Context.Trace2, "User canceled device code authentication");
                }

                // Close the dialog
                promptCts.Cancel();

                return tokenResult;
            }

            return await GetTokenByDeviceCodeViaTtyAsync(client, dcr);
        }

        private Task ShowDeviceCodeViaUiAsync(OAuth2DeviceCodeResult dcr, CancellationToken ct)
        {
            var viewModel = new DeviceCodeViewModel(Context.SessionManager)
            {
                UserCode = dcr.UserCode,
                VerificationUrl = dcr.VerificationUri.ToString(),
            };

View on GitHub (pinned to e8ce762cd0)