{"record":{"id":"d4aeaa4aa410668c","repo":"git-ecosystem/git-credential-manager","slug":"user-canceled-device-code-authentication-d4aeaa","errorCode":null,"errorMessage":"User canceled device code authentication","messagePattern":"User canceled device code authentication","errorType":"exception","errorClass":"Trace2InvalidOperationException","httpStatus":null,"severity":"warning","filePath":"src/GitHub/GitHubAuthentication.cs","lineNumber":484,"sourceCode":"                // Start the request for an OAuth token but don't wait\n                Task<OAuth2TokenResult> tokenTask = oauthClient.GetTokenByDeviceCodeAsync(dcr, tokenCts.Token);\n\n                Task t = await Task.WhenAny(promptTask, tokenTask);\n\n                // If the dialog was closed the user wishes to cancel the request\n                if (t == promptTask)\n                {\n                    tokenCts.Cancel();\n                }\n\n                OAuth2TokenResult tokenResult;\n                try\n                {\n                    tokenResult = await tokenTask;\n                }\n                catch (OperationCanceledException)\n                {\n                    throw new Trace2InvalidOperationException(Context.Trace2,\n                        \"User canceled device code authentication\");\n                }\n\n                // Close the dialog\n                promptCts.Cancel();\n\n                return tokenResult;\n            }\n\n            return await GetOAuthTokenViaDeviceCodeViaTtyAsync(oauthClient, dcr);\n        }\n\n        private Task ShowDeviceCodeViaUiAsync(OAuth2DeviceCodeResult dcr, CancellationToken ct)\n        {\n            var viewModel = new DeviceCodeViewModel(Context.SessionManager)\n            {\n                UserCode = dcr.UserCode,\n                VerificationUrl = dcr.VerificationUri.ToString(),","sourceCodeStart":466,"sourceCodeEnd":502,"githubUrl":"https://github.com/git-ecosystem/git-credential-manager/blob/e8ce762cd04b4100ae637b5fbf39ef9d0a96561e/src/GitHub/GitHubAuthentication.cs#L466-L502","documentation":"During the GitHub OAuth device-code flow, GetOAuthTokenViaDeviceCodeAsync awaits the token task while showing a prompt to the user. If the user cancels the operation (closes the dialog or cancels the device-code prompt), the awaited task throws OperationCanceledException, which the code rethrows as a Trace2InvalidOperationException with this message.","triggerScenarios":"User presses Cancel or closes the device-code dialog window while GCM is polling GitHub for the device-flow token; the CancellationToken passed to the device prompt is triggered before the token result arrives.","commonSituations":"Users abandoning the 'enter code at github.com/login/device' flow because the dialog was confusing or they wanted to use a PAT instead; automation that programmatically cancels the prompt CTS after a timeout.","solutions":["Retry the login and complete the device-code entry at https://github.com/login/device before the code expires.","Use a Personal Access Token instead: run 'git-credential-manager github login --pat' or set the credential directly.","If cancellation was unintentional from tooling, remove code that cancels the prompt CancellationToken (promptCts) while the token task is pending.","Clear stale credentials and re-run authentication: 'git-credential-manager github logout' then log in again."],"exampleFix":"// before (user closed device dialog, token never obtained)\n// after: choose a non-interactive path or complete the flow\ngit-credential-manager github logout\ngit-credential-manager github login --device   // then enter the code promptly","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try\n{\n    var token = await githubAuth.GetOAuthTokenViaDeviceCodeAsync(targetUri, scopes);\n}\ncatch (OperationCanceledException)\n{\n    // user cancelled the device-code prompt; fall back to PAT flow\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"User canceled device code\"))\n{\n    // same: prompt user to retry or supply a PAT\n}","preventionTips":["Instruct users to complete the code entry before the device code expires","Offer a PAT fallback whenever device flow may be cancelled","Avoid programmatically cancelling the prompt CTS while the token task is pending","Show clear instructions with the device code URL and expiry"],"tags":["github","oauth","device-code","user-cancellation"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"e8ce762cd04b4100ae637b5fbf39ef9d0a96561e","analyzedAt":"2026-09-11T17:15:08.753Z","contentChangedAt":"2026-09-11T17:15:08.753Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}