{"record":{"id":"fa989e9138b532c0","repo":"git-ecosystem/git-credential-manager","slug":"user-canceled-device-code-authentication","errorCode":null,"errorMessage":"User canceled device code authentication","messagePattern":"User canceled device code authentication","errorType":"exception","errorClass":"Trace2Exception","httpStatus":null,"severity":"warning","filePath":"src/Core/Authentication/OAuthAuthentication.cs","lineNumber":229,"sourceCode":"                // Start the request for an OAuth token but don't wait\n                Task<OAuth2TokenResult> tokenTask = client.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 Trace2Exception(Context.Trace2, \"User canceled device code authentication\");\n                }\n\n                // Close the dialog\n                promptCts.Cancel();\n\n                return tokenResult;\n            }\n\n            return await GetTokenByDeviceCodeViaTtyAsync(client, 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(),\n            };","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/git-ecosystem/git-credential-manager/blob/e8ce762cd04b4100ae637b5fbf39ef9d0a96561e/src/Core/Authentication/OAuthAuthentication.cs#L211-L247","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the device code flow and complete authorization before the code expires","Do not close the device-code prompt dialog until authorization finishes","In automation, avoid canceling the tokenTask's CancellationToken prematurely","Handle this exception as a benign user-abort rather than a system failure"],"exampleFix":"// before\ntokenResult = await oauth.GetTokenByDeviceCodeAsync(client, scopes);\n// after\ntry\n{\n    tokenResult = await oauth.GetTokenByDeviceCodeAsync(client, scopes);\n}\ncatch (Exception ex) when (ex.Message.Contains(\"User canceled\"))\n{\n    // user aborted; fall back to device code retry or another flow\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"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 */ }","preventionTips":["Do not dismiss the device-code dialog prematurely","Complete authorization before the device code expires","In automation, keep the prompt's CancellationToken alive until auth completes"],"tags":["oauth","device-code","cancellation","user-abort"],"backgroundTag":"operation-cancelled","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"}